退出登录
我们只需要定义一个登录接口,然后获取SecurityContextHolder中的认证信息,删除redis中对应的数据即可。
LoginController控制层
@RestController public class LoginController { @Autowired private LoginService loginService; @RequestMapping("/user/login") public ResponseResult login(@RequestBody User user) { // 登录 return loginService.login(user); } // 退出登录 @RequestMapping("/user/logout") public ResponseResult logout() { return loginService.logout(); } }
LoginService业务层接口
public interface LoginService { // 登录 ResponseResult login(User user); // 退出登录 ResponseResult logout(); }
LoginServiceImpl业务层实现类
@Service public class LoginServiceImpl implements LoginService { @Autowired private AuthenticationManager authenticationManager; @Autowired private RedisCache redisCache; // 登录 @Override public ResponseResult login(User user) { //AuthenticationManager authenticate进行用户认证 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword()); Authentication authenticate = authenticationManager.authenticate(authenticationToken); // 如果认证没通过,给出对应的提示 if (Objects.isNull(authenticate)) { throw new RuntimeException("登录失败"); } // 如果认证通过了,使用userid生成一个jwt jwt存入ResponseResult返回 LoginUser loginUser = (LoginUser) authenticate.getPrincipal(); String userid = loginUser.getUser().getId().toString(); String jwt = JwtUtil.createJWT(userid); HashMap<String, String> map = new HashMap<>(); map.put("token",jwt); // 把完成的用户信息存入redis userid作为key redisCache.setCacheObject("login:"+userid,loginUser); return new ResponseResult(200,"登录成功",map); } // 退出登录 @Override public ResponseResult logout() { // 获取SecurityContextHolder中的用户id UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); LoginUser loginUser = (LoginUser) authentication.getPrincipal(); Long userid = loginUser.getUser().getId(); // 删除redis中的值 redisCache.deleteObject("login:" + userid); return new ResponseResult(200,"注销成功"); } }
认证配置详解
SecurityConfig配置类
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { //创建BCryptPasswordEncoder注入容器 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Autowired private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; @Override protected void configure(HttpSecurity http) throws Exception { // http // //关闭csrf // .csrf().disable() // //不通过Session获取SecurityContext // .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // .and() // .authorizeRequests() // // 对于登录接口 允许匿名访问 // .antMatchers("/user/login").anonymous() // // 除上面外的所有请求全部需要鉴权认证 // .anyRequest().authenticated(); http .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/hello").permitAll() .antMatchers("/user/login").anonymous() .anyRequest().authenticated(); // 把token校验过滤器添加到过滤器链中 http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
权限系统的作用
例如一个学校图书馆的管理系统,如果是普通学生登录就能看到借书还书相关的功能,不可能让他看到并且去使用添加书籍信息,删除书籍信息等功能。但是如果是一个图书馆管理员的账号登录了,应该就能看到并使用添加书籍信息,删除书籍信息等功能。
总结起来就是不同的用户可以使用不同的功能。这就是权限系统要去实现的效果。
我们不能只依赖前端去判断用户的权限来选择显示哪些菜单那些按钮。因为如果只是这样,如果有人知道了对应功能的接口地址就可以不通过前端,直接去发送请求来实现相关功能操作。
所以我们还需要再后台进行用户权限的判断,判断当前用户是否有相应的权限,必须具有所需权限才能进行相应的操作。
授权基本流程
在SpringSecurity中,会使用默认的FilterSecurityInterceptor来进行权限校验。在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication,然后获取其中的权限信息。当前用户是否拥有访问当前资源所需的权限。
所以我们在项目中只需要把当前登录用户的权限信息也存入Authentication。