SpringBoot+SpringSecurity系列八:整合JWT #yyds干货盘点#

Posted 梁云亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot+SpringSecurity系列八:整合JWT #yyds干货盘点#相关的知识,希望对你有一定的参考价值。

数据库:

第一步:当用户在没有授权时返回的指定信息的Handler

@Slf4j
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler 
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws ServletException 
        log.info("用户访问没有授权资源:", e.getMessage());

        response.setContentType("application/json;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        try (PrintWriter out = response.getWriter();) 
            Result result = ResultUtil.fail("用户访问未授权资源").setCode(HttpServletResponse.SC_UNAUTHORIZED);
            out.write(JsonUtil.obj2String(result));
            out.flush();
         catch (IOException exception) 
            log.error(e.getMessage());
            e.printStackTrace();
        
    

第二步:访问受限资源时未登录或未携带正确token时返回信息的EntryPoint

@Slf4j
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint 
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws ServletException, IOException 
        log.info("登录失败or用户访问资源没有携带正确的token:", e.getMessage());
        response.setContentType("application/json;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        try (PrintWriter out = response.getWriter();) 
            Result result = ResultUtil.fail("用户访问资源没有携带正确的token").setCode(HttpServletResponse.SC_UNAUTHORIZED);
            out.write(JsonUtil.obj2String(result));
            out.flush();
         catch (IOException exception) 
            log.error(e.getMessage());
            e.printStackTrace();
        
    

第三步:定义JWT认证过滤器

@Slf4j
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter 
    @Resource
    private JwtUtil jwtUtil;

    @Resource
    private UserDetailsServiceImpl userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException 
        String token = request.getHeader(jwtUtil.getHeader());
        log.info("header token:", token);
        //如果请求头中有token,则进行解析,并且设置认证信息
        if (token != null && token.trim().length() > 0) 
            //根据token获取用户名
            String username = jwtUtil.getSubjectFromToken(token);
            // 验证username,如果验证合法则保存到SecurityContextHolder
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) 
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                // JWT验证通过,使用Spring Security 管理
                if (jwtUtil.validateToken(token, userDetails)) 
                    //加载用户、角色、权限信息
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                
            
        

        //如果请求头中没有Authorization信息则直接放行
        chain.doFilter(request, response);
    

第四步:自定义UserDetailsService

@Service
public class UserDetailsServiceImpl implements UserDetailsService 
    @Resource
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 
        MenuItemAuth menuItemAuth = userService.getAuthoritiesPermissions(username);
        System.out.println(JsonUtil.obj2String(menuItemAuth));
        UserDetails userDetails = org.springframework.security.core.userdetails.User.withUsername(username)
                .password(menuItemAuth.getPassword())
                .authorities(menuItemAuth.getAuth().split(","))
                .build();

        //UserDetails userDetails = new org.springframework.security.core.userdetails.User(username, passwordEncoder.encode("1234"),
        //        AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_vip,user:list,user:update"));
        return userDetails;
    

相关的代码如下所示:

  • MenuItem.java

    @Getter
    @Setter
    @Builder
    @ToString
    @AllArgsConstructor
    @NoArgsConstructor
    public class MenuItem 
    /**
     * 编号
     */
    private Integer id;
    
    /**
     * 名称
     */
    private String name;
    
    /**
     * 权限名称
     */
    private String code;
    
    /**
     * 父编号
     */
    private Integer pid;
    
  • MenuItemAuth.java

    @Getter
    @Setter
    @ToString
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public class MenuItemAuth 
    /**
     * 后台管理系统菜单项列表
     */
    List<MenuItem> menuItemList;
    
    /**
     * 用户的密码
     */
    private String password;
    
    /**
     * 用户角色code+权限code的字符串
     */
    private String auth;
    

第五步:定义SpringSecurity配置类

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityJwtConfig extends WebSecurityConfigurerAdapter 
    @Bean
    public PasswordEncoder passwordEncoder() 
        return new BCryptPasswordEncoder();
    

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception 
        return super.authenticationManagerBean();
    

    @Resource
    private JwtAccessDeniedHandler jwtAccessDeniedHandler;

    @Resource
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Resource
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.csrf().disable();
        // 禁用session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.authorizeRequests()
                //login 不拦截
                .antMatchers("/user/login").permitAll()
                .antMatchers("/user/login0").permitAll()
                .antMatchers("/init/redis").permitAll()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/").permitAll()
                .anyRequest().authenticated();

        //用户访问没有授权资源
        http.exceptionHandling().accessDeniedHandler(jwtAccessDeniedHandler);
        //授权错误信息处理
        //用户访问资源没有携带正确的token
        http.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint);
        // 使用自己定义的拦截机制验证请求是否正确,拦截jwt
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    

Controller

@RestController
@RequestMapping("/authority")
public class AuthorityController 

    @PostMapping("/login")
    public Result login(@RequestBody User user) 
        // 登陆验证
        UsernamePasswordAuthenticationToken token0 =
                new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
        Authentication authentication = authenticationManager.authenticate(token0);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        //生成token,返回给客户端
        MenuItemAuth menuItemAuth = userService.getAuthoritiesPermissions(user.getUsername());
        UserDetails userDetails = org.springframework.security.core.userdetails.User.withUsername(user.getUsername())
                .password(menuItemAuth.getPassword())
                .authorities(menuItemAuth.getAuth().split(","))
                .build();

        String token = jwtUtil.generateToken(userDetails, user1.getId() + "");

        return ResultUtil.success().setData("token", token);
    

    @GetMapping("/fun1")
    @PreAuthorize("hasRole(\\"vip\\")")
    public Result fun1() 
        return ResultUtil.success("fun1");
    

    @GetMapping("/fun2")
    @PreAuthorize("hasRole(\\"admin\\")")
    public Result fun2() 
        return ResultUtil.success("fun2");
    

    @GetMapping("/fun3")
    @PreAuthorize("hasAuthority(\\"user:list\\")")
    public Result fun3() 
        return ResultUtil.success("fun3");
    

    @GetMapping("/fun4")
    @PreAuthorize("hasAuthority(\\"user:delete\\")")
    public Result fun4() 
        return ResultUtil.success("fun4");
    

相关代码

@Service
@CacheConfig(cacheManager = "cacheManager")
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService 
    @Resource
    RedisService<User> userRedisServiceImpl;
    @Resource
    private RoleService roleService;

    @Resource
    private UserPermissionService userPermissionService;
    @Resource
    private PermissionService permissionService;
    @Resource
    private RolePermissionService rolePermissionService;

    @Override
    public MenuItemAuth getAuthoritiesPermissions(String username) 
        List<MenuItem> menuItemList = new ArrayList<>();

        //根据用户名查找用户
        User user = baseMapper.selectOne(new QueryWrapper<User>()
                .select("id", "username", "password", "nickname", "email", "tel", "gender", "birth", "avatar", "role_id")
                .eq("username", username)
                .eq("status", BaseStatus.Status.ok));

        //查找角色
        Role role = roleService.getOne(new QueryWrapper<Role>()
                .select("id", "name", "code")
                .eq("status", BaseStatus.Status.ok)
                .eq("id", user.getRoleId()));
        menuItemList.add(MenuItem.builder()
                .id(role.getId())
                .name(role.getName())
                .code(role.getCode())
                .pid(-1) // -1表示是角色
                .build());
        String authorities = "ROLE_" + role.getCode() + ",";

        // 当前用户拥有的权限permission的id的集合
        List<Integer> permissionIdList = new ArrayList<>();
        //在tb_role_permission中查找Role所对应的角色
        List<RolePermission> rolePermissionList = rolePermissionService.list(new QueryWrapper<RolePermission>()
                .select("permission_id")
                .eq("role_id", role.getId())
                .eq("status", BaseStatus.Status.ok));
        List<Integer> pid1 = rolePermissionList.stream().map(item -> item.getPermissionId()).collect(Collectors.toList());
        permissionIdList.addAll(pid1);
        //在tb_user_permission中查找权限
        List<UserPermission> userPermissionList = userPermissionService.list(new QueryWrapper<UserPermission>()
                .select("permission_id")
                .eq("user_id", user.getId())
                .eq("status", BaseStatus.Status.ok));
        List<Integer> pid2 = userPermissionList.stream().map(item -> item.getPermissionId()).collect(Collectors.toList());
        permissionIdList.addAll(pid2);

        //查找具体的权限信息
        List<Permission> permissionList = permissionService.list(new QueryWrapper<Permission>()
                .select("id", "name", "code", "pid")
                .eq("status", BaseStatus.Status.ok)
                .in("id", permissionIdList));
        List<MenuItem> permissionMenuItemList = permissionList.stream().map(item -> MenuItem.builder()
                .id(item.getId())
                .name(item.getName())
                .code(item.getCode())
                .pid(item.getPid())
                .build()).collect(Collectors.toList());
        menuItemList.addAll(permissionMenuItemList);

        String permissions = permissionList.stream().map(item -> item.getCode() + ",").collect(Collectors.joining());
        System.out.println(permissions);
        //去掉最后一个逗号
        permissions = permissions.substring(0, permissions.length() - 1);

        System.out.println(authorities + permissions);
        MenuItemAuth res = MenuItemAuth.builder()
                .menuItemList(menuItemList)
                .auth(authorities + permissions)
                .password(user.getPassword())
                .build();
        return res;
    

以上是关于SpringBoot+SpringSecurity系列八:整合JWT #yyds干货盘点#的主要内容,如果未能解决你的问题,请参考以下文章

Java 之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发三系统权限

使用篇二SpringBoot集成SpringSecurity(22)

SpringBoot整合SpringSecurity

SpringBoot集成SpringSecurity+CAS

SpringBoot SpringSecurity 介绍(基于内存的验证)

Java 之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发三系统权限