SpringBoot使用Security认证框架(2.使用)

蚊子 2023年03月02日 371次浏览

前言

需要的工具类,请前往:https://www.0po.cn/archives/24
本文章是使用方法,数据库查询使用的是mybatis-plus
mybatis-plus依赖

<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>

为什么mybatis-plus依赖要在这里写?
因为:

  1. 如果你的公司不允许使用mybatis-plus,而使用的是mybatis,那么下面代码,只能对你仅供参考了…
  2. 你只能参考,用mybatis自已写一个

开始

注意:我的包名叫com.zb,凡是报错的类,记得把包换成你的包

1.UserService里面有一个方法:根据用户名和密码查询,返回token
注意:不要想着直接写自已登录的Service里面,不行,因为下面要实现一个类叫UserDetailsService,你自已登录的Service里面无法同时实现2个

     String login(String username, String passwd);

image-1677765585917

2.UserDetailServiceImpl继承UserDetailsService
实现public UserDetails loadUserByUsername方法,Security会自动找谁实现了,然后获取信息,注意看注释
image-1677765842108
代码:

package com.zb.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zb.entity.LoginUser;
import com.zb.entity.Menu;
import com.zb.entity.User;
import com.zb.mapper.MenuMapper;
import com.zb.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserDetailServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;

 	//权限认证表,不需要权限直接删除即可
    @Autowired
    private MenuMapper menuMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("2.进入访问数据库的UserDetailServiceImpl服务类中...");
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("nick_name", username);
        //根据用户名查询用户信息
        User user = userMapper.selectOne(queryWrapper);
        
        //权限认证,如不需要权限搞权限,直接删除即可
        //拿用户名 查用户权限等信息
        List<Menu> query = menuMapper.query(user.getId().toString());
        //读取query里面权限信息(Perms参数),将信息存入list
        List<String> list = query.stream().map(menu -> {
            return menu.getPerms();
        }).collect(Collectors.toList());
			//权限认证,如不需要权限搞权限,删到这里

        if (user != null) {
            //将用户信息,权限信息放入LoginUser
            //如不要权限认证,要把下面list参数删除
            //然后去LoginUser类,24行和31行,删的东西如下:
            //LoginUser(AdminEntity user, List<String> powers)方法,删除List<String> powers参数
            //Collection<? extends GrantedAuthority> getAuthorities()直接把这个方法返回null即可
            return new LoginUser(user, list);
        }
        return null;
    }
}

3.UserServiceImpl继承自已的UserService,实现自已的login方法
注意:看42-45行
image-1677765959529

package com.zb.service.impl;

import com.alibaba.fastjson.JSON;
import com.zb.config.JwtUtil;
import com.zb.entity.LoginUser;
import com.zb.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private RedisTemplate redisTemplate;


    @Autowired
    //发起验证,此方法已经实例化
    private AuthenticationManager authenticationManager;

    @Override
    public String login(String username, String passwd) {
        //使用密码器,并且告诉密码器登录的账号密码
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(username, passwd);
        //告诉验证起,本次登录使用的是密码器验证
        //这里会调用:实现UserDetailsService接口的UserDetails类 loadUserByUsername的方法
        //本意就是已经查出数据了
        Authentication authenticate = authenticationManager
                .authenticate(usernamePasswordAuthenticationToken);

        if (ObjectUtils.isEmpty(authenticate)) {
            return "登陆失败";
        }
        System.out.println("3.登陆成功信息");
        //获取数据,转成LoginUser对象
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        //拿出用户名,生成token
        String token = JwtUtil.createJWT(loginUser.getUser().getNickName());
        //将用户名和用户信息存入redis
        redisTemplate.boundValueOps("token:" + loginUser.getUser().getNickName()).set(JSON.toJSONString(loginUser));
        return token;
    }
}

4.Controller实现刚开始创建UserService方法即可,它会返回一个token,普通方法必须传入token,否则会被拦截
image-1677766200796

package com.zb.controller;

import com.zb.entity.LoginUser;
import com.zb.entity.User;
import com.zb.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisTemplate redisTemplate;

    @PostMapping("/login")
    public String login(@RequestParam("username") String username, @RequestParam("passwd") String passwd) {
        return userService.login(username, passwd);
    }

    @GetMapping("/info")
    public User info() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        return loginUser.getUser();
    }

    @GetMapping("/exit")
    public String exit() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        String key = "token:" + loginUser.getUser().getNickName();
        redisTemplate.delete(key);
        return "success";
    }

    @GetMapping("/testok")
    public String tsetok(@RequestHeader String token){
        System.out.println("这是一个普通方法");
        return "这是一个普通方法";
    }

}

5.权限拦截,使用@PreAuthorize(“”)注解,里面填的是存放LoginUser类里面查出来的powers权限名字
如果不要权限认证,这里就不用看了
image-1677766272733

package com.zb.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("/show")
   @PreAuthorize("hasAuthority('book:search') or hasAuthority('book:get')")
    public String show() {
        System.out.println("============>");
        return "hello";
    }
}