SpringBoot+SpringSecurity+JWT前后端分离(一)

SpringBoot+SpringSecurity+JWT前后端分离(一)

  • 1、引入坐标
  • 2、实现UserDetails接口实体
  • 3、实现UserDetailsService接口,查询用户信息
  • 4、JWT工具类
  • 5、自定义handler处理器
    • 5.1 登录成功处理器(如果自定义controller登录接口,那就不需要登录成功处理器)
    • 5.2 登录失败处理器(如果自定义controller登录接口,那就不需要登录失败处理器)
    • 5.3 没有登录处理器
    • 5.4 没有权限处理器
    • 5.5 退出处理器
  • 6、JWT过滤器(校验解析token过滤器,两种用其一即可,注意配置)
    • 6.1:继承类: BasicAuthenticationFilter
    • 6.2:继承类: OncePerRequestFilter
    • 6.3继承 GenericFilterBean
  • 7、自定义登录验证认证提供者,认证用户权限
  • 8、SpringSecurity核心配置
  • 9 、SpringSecurity详细版本

1、引入坐标

<dependency>
	 <groupId>org.springframework.bootgroupId>
     <artifactId>spring-boot-starter-securityartifactId>
dependency>

2、实现UserDetails接口实体

/*
 * @description: 用户信息封装
 */
@Data
public class SecurityUser implements Serializable, UserDetails {

    private String username;  //用户名

    private String password; //密码

    private List<String> roleName;  //角色

    private Collection<? extends GrantedAuthority> authorities;  //权限


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

3、实现UserDetailsService接口,查询用户信息

/*
 * @description: 查询用户信息以权限
 */
@Service
public class SecurityUserDetails implements UserDetailsService {


    @Autowired
    private UserManageMapper userManageMapper;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserManage user = userManageMapper.selectOne(new QueryWrapper<UserManage>().eq("username", username));

        if (user==null){
            throw new RuntimeException("用户名不存在");
        }

        List<String> roleNames = userManageMapper.findRoleNames(user.getUsername());

        List<GrantedAuthority> authorities = new ArrayList<>();

        for (String roleName : roleNames) {
            authorities.add(new SimpleGrantedAuthority("ROLE_"+roleName));
        }


        SecurityUser securityUser = new SecurityUser();

        securityUser.setUsername(user.getUsername());
        securityUser.setPassword(user.getCryp());
        securityUser.setAuthorities(authorities);

        System.out.println("系统管理:用户管理========"+securityUser);
        return securityUser;

    }
}

4、JWT工具类

/*
 * @description: JWT工具类
 */
public class JwtUtil {


    private final static long excTime = 604800000;  // 7天过期

    private static final String secretKey = "jhdr";  //秘钥



    /*
     * @description: 生成JWT
     * @param null
     * @return
     */
    public static String createJwt(String jti,String username,Long timeMillis){

        Date nowDate = new Date();

        long nowTimeMillis = System.currentTimeMillis();

        if (timeMillis == null) {

            timeMillis = excTime;
        }

        long excTimeMillis = timeMillis + nowTimeMillis;

        Date excDate = new Date(excTimeMillis);

        JwtBuilder builder = Jwts.builder()
                .setId(jti)  //唯一的ID
                .setSubject(username)  //主题
                .setIssuer("daishan")  //签发者
                .setIssuedAt(nowDate)  //签发日期
                .setExpiration(excDate)  //设置过期时间
                .signWith(SignatureAlgorithm.HS256, generalKey()); //使用HS256对称加密算法签名, 第二个参数为秘钥
        return builder.compact();
    }


    /*
     * @description: 生成JWT
     * @param null
     * @return
     */
    public static String createJwt(SecurityUser securityUser, Long timeMillis){

        Date nowDate = new Date();

        long nowTimeMillis = System.currentTimeMillis();

        if (timeMillis == null) {

            timeMillis = excTime;
        }

        long excTimeMillis = timeMillis + nowTimeMillis;

        Date excDate = new Date(excTimeMillis);

        JwtBuilder builder = Jwts.builder()
                .setSubject(securityUser.getUsername())  //主题
                .setIssuer("daishan")  //签发者
                .setIssuedAt(nowDate)  //签发日期
                .setExpiration(excDate)  //设置过期时间
                .signWith(SignatureAlgorithm.HS256, generalKey()); //使用HS256对称加密算法签名, 第二个参数为秘钥
        return builder.compact();
    }



    /*
     * @description: 解析JWT
     * @param null
     * @return
     */
    public static Claims parserJwt(String jwt){
        return Jwts.parser()
                .setSigningKey(generalKey())
                .parseClaimsJws(jwt)
                .getBody();
    }




    /*
     * @description: 生成加密后的秘钥 secretKey
     * @param null
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodeKey = Base64.getDecoder().decode(secretKey);
        SecretKey key = new SecretKeySpec(encodeKey,0,encodeKey.length,"AES");
        return key;
    }

5、自定义handler处理器

5.1 登录成功处理器(如果自定义controller登录接口,那就不需要登录成功处理器)

/**
 * 登录成功处理
 */
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {



    @Autowired
    private SecurityUserDetails securityUserDetails;


    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        httpServletResponse.setContentType("applicatiopn/json;charset=utf-8");
        PrintWriter writer = httpServletResponse.getWriter();
        SecurityUser userDetailsUser = (SecurityUser) authentication.getPrincipal();
        UserDetails userDetails = securityUserDetails.loadUserByUsername(authentication.getName());
        System.out.println(userDetails);
        String token = JwtUtil.createJwt(userDetailsUser,null);
        Map<String,Object> map = new HashMap<>();
        map.put("cood","200");
        map.put("msg","登录成功");
        map.put("token",token);
        map.put("userDetails",userDetails);
        map.put("username",authentication.getName());
        writer.println(JSON.toJSONString(map));
        writer.flush();
        writer.close();
    }
}

5.2 登录失败处理器(如果自定义controller登录接口,那就不需要登录失败处理器)

/**
 * 登录失败处理
 */
@Component
public class LoginFailHandler implements AuthenticationFailureHandler {



    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.setContentType("applicatiopn/json;charset=utf-8");
        PrintWriter writer = httpServletResponse.getWriter();
        Map<String,Object> map = new HashMap<>();
        map.put("cood","400");
        map.put("msg","用户名或密码错误");
        writer.println(JSON.toJSONString(map));
        writer.flush();
        writer.close();
    }
}

5.3 没有登录处理器

/**
 * 未登录处理
 */
@Component
public class NotLoginHandler implements AuthenticationEntryPoint {


    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.setContentType("applicatiopn/json;charset=utf-8");
        PrintWriter writer = httpServletResponse.getWriter();
        Map<String,Object> map = new HashMap<>();
        map.put("cood","405");
        map.put("msg","请先登录");
        writer.println(JSON.toJSONString(map));
        writer.flush();
        writer.close();
    }
}

5.4 没有权限处理器

/**
 * 没有权限处理
 */
@Component
public class NotAccessDeniedHandler implements AccessDeniedHandler {


    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        httpServletResponse.setContentType("applicatiopn/json;charset=utf-8");
        PrintWriter writer = httpServletResponse.getWriter();
        Map<String,Object> map = new HashMap<>();
        map.put("cood","403");
        map.put("msg","没有权限访问或操作,请联系管理员");
        writer.println(JSON.toJSONString(map));
        writer.flush();
        writer.close();
    }
}

5.5 退出处理器

/**
 * 退出登录
 */
@Component
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {


    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        httpServletResponse.setContentType("applicatiopn/json;charset=utf-8");
        PrintWriter writer = httpServletResponse.getWriter();
        Map<String,Object> map = new HashMap<>();
        map.put("cood","200");
        map.put("msg","退出成功");
        writer.println(JSON.toJSONString(map));
        writer.flush();
        writer.close();
    }
}

6、JWT过滤器(校验解析token过滤器,两种用其一即可,注意配置)

6.1:继承类: BasicAuthenticationFilter

/*
 *	拦截器
 */
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

    private UserDetailsService userDetailsService;


    // 从 Spring Security 配置文件那里传过来
    public JwtAuthorizationFilter(AuthenticationManager authenticationManager, UserDetailsService userDetailsService) {
        super(authenticationManager);
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 获取当前认证成功的权限信息
        UsernamePasswordAuthenticationToken authentication = getAuthentication(request);

        if (authentication !=null){
             //设置springsecurity上下文中
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        chain.doFilter(request,response);
    }



    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) throws IOException {

        try {
             
             //获取请求头token
            String token = request.getHeader("token");

            if (token !=null){
                //解析Token
                Claims claims = JwtUtil.parserJwt(token);
                String username = claims.getSubject(); //获取用户名
                UserDetails userDetails = userDetailsService.loadUserByUsername(username); //获取用户信息以及角色
                Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities(); //获取权限
                return new UsernamePasswordAuthenticationToken(userDetails,token,authorities); //返回用户以及权限
            }
        } catch (UsernameNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

配置如下:


	@Autowired
    private SecurityUserDetails securityUserDetails;

 	@Override
    protected void configure(HttpSecurity http) throws Exception {
    	// addFilterBefore也可以
        http.addFilter(new JwtAuthorizationFilter(authenticationManager(),securityUserDetails));
        

6.2:继承类: OncePerRequestFilter

/**
 * @description: 拦截器
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private SecurityUserImpl securityUser;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String token = request.getHeader("token");
        if (token !=null){

            Claims claims = JwtUtil.parserJwt(token);
            String username = claims.getSubject();
            UserDetails userDetails = securityUser.loadUserByUsername(username);
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,userDetails.getPassword(),userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); //这个可写可不写
            //用户信息以及权限设置到上下文中
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        filterChain.doFilter(request,response); //放行
    }
}

配置如下:

 
	@Autowired
    private JwtAuthorizationTokenFilter jwtAuthorizationTokenFilter;

	@Override
    protected void configure(HttpSecurity http) throws Exception {
    	// 只能是addFilterBefore
        http.addFilterBefore(jwtAuthorizationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        

6.3继承 GenericFilterBean

public class Filter extends GenericFilterBean {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
		//逻辑都是一样的,配置只能配置addFilterBefore,不能配置addFilter
 	}
}

7、自定义登录验证认证提供者,认证用户权限

@Configuration
public class MyAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private SecurityUserDetails securityUserDetails;

    @Autowired
    private PasswordEncoder passwordEncoder;


    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        String userName = authentication.getName();// 这个获取表单输入中的用户名
        String password = (String) authentication.getCredentials();
        UserDetails userDetails = securityUserDetails.loadUserByUsername(userName);
        String encodePassword = passwordEncoder.encode(password);
        if (!passwordEncoder.matches(password,userDetails.getPassword())) {
            throw new UsernameNotFoundException("用户名或者密码不正确");
        }

        Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();

        System.out.println("=============用户名:"+userName);
        System.out.println("=============密码:"+password);
        System.out.println("=============UserDetails数据集合:"+userDetails);
        System.out.println("=============权限为:"+authorities);


        return new UsernamePasswordAuthenticationToken(userDetails, encodePassword, authorities);

    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

8、SpringSecurity核心配置

/**
 * Spring Security 配置类
 * @EnableGlobalMethodSecurity 开启注解的权限控制,默认是关闭的。
 * prePostEnabled:使用表达式实现方法级别的控制,如:@PreAuthorize("hasRole('ADMIN')")
 * securedEnabled: 开启 @Secured 注解过滤权限,如:@Secured("ROLE_ADMIN")
 * jsr250Enabled: 开启 @RolesAllowed 注解过滤权限,如:@RolesAllowed("ROLE_ADMIN")
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private LoginSuccessHandler loginSuccessHandler; //登录成功

    @Autowired
    private LoginFailHandler loginFailHandler; //登录失败

    @Autowired
    private LogoutSuccessHandlerImpl logoutSuccessHandler;  //退出成功

    @Autowired
    private NotAccessDeniedHandler notAccessDeniedHandler;  //没有权限处理

    @Autowired
    private NotLoginHandler notLoginHandler; //未登录处理

    @Autowired
    private MyAuthenticationProvider myAuthenticationProvider;

    @Autowired
    private SecurityUserDetails securityUserDetails;

  	@Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }





    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth.authenticationProvider(myAuthenticationProvider);  //认证提供者自定义认证
        auth.userDetailsService(securityUserDetails);  //实现UserDetails类认证
    }




	/**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable();

        //禁用缓存
//        http.headers().cacheControl();

        // 基于Token不需要session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);


        http.formLogin().loginProcessingUrl("/user/login").usernameParameter("username").passwordParameter("password");
					//如果conroller写了自定义接口,登录成功失败处理器可以不用写
//                .successHandler(loginSuccessHandler).failureHandler(loginFailHandler); 


        http.logout().logoutUrl("/user/logout").logoutSuccessHandler(logoutSuccessHandler);

        http.httpBasic().authenticationEntryPoint(notLoginHandler);

        http.exceptionHandling().accessDeniedHandler(notAccessDeniedHandler);

        // 添加JWT过滤器
          // 继承 BasicAuthenticationFilter类的拦截器配置
//       http.addFilter(new JwtAuthorizationFilter(authenticationManager(),securityUserDetails));

        // 继承 OncePerRequestFilter类的拦截器配置
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);


    }



    private static final String [] excludeUrl = {"/user/login","/doc.html/**","/swagger-resources/**","/v2/**","/webjars/**","/image/**"};

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(excludeUrl);
    }
    

9 、SpringSecurity详细版本

https://blog.csdn.net/yuanlaijike/category_9283872.html

你可能感兴趣的:(java)