栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

springsecurity

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

springsecurity

Spring Security+JWT登录的三种方式 1、访问接口/demo01
  • 当用户从浏览器发送请求访问/demo01接口时,服务端会返回302响应码,让客户端重定向到/login页面,用户在/login页面登录,登陆成功之后,就会自动跳转到/demo01接口。

  • 默认的账户是admin,密码会在项目启动的时候在控制台自动生成。

2、在配置文件中自定义账号和密码
  • 配置完成后重启项目就可以使用自定义的账号密码登录了
server:
  port: 8888
spring:
  security:
    user:
      name: admin
      password: admin
Java 配置用户名/密码
  • 自定义配置类继承WebSecurityConfigurerAdapter,实现config方法。(注意:要是不添加roles启动会报错——Cannot pass a null GrantedAuthority collection)
  • Spring Security中提供了 BCryptPasswordEncoder 密码编码工具,可以非常方便的实现密码的加密加盐,相同明文加密出来的结果总是不同,这样就不需要用户去额外保存盐的字段了
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //下面的配置表示在内存中配置用户
        auth.inMemoryAuthentication()
                .withUser("jack")
                .password(passwordEncoder().encode("123"))
                .roles("admin");
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
3、详细登录配置以及配置类中的所有方法以及实体
  • 对于登录接口,登录成功后的响应,登录失败后的响应,我们都可以在 WebSecurityConfigurerAdapter 的实现类中进行配置。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAuthenticationProvider authenticationProvider;

    
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        expressionUrlAuthorizationConfigurer.expressionInterceptUrlRegistry registry = http
                .authorizeRequests();

        // 白名单方法(所有放行的接口)
        for (String url : ignoreUrlsConfig().getUrls()) {
            registry.antMatchers(url).permitAll();
        }

        registry
                // 放行所有OPTIONS请求
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                // 其他请求都需要认证后才能访问
                .anyRequest().authenticated()
            
                // 使用自定义的 accessDecisionManager
                .accessDecisionManager(accessDecisionManager())
                .and()
                // 添加未登录与权限不足异常处理器
                .exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler())
                .authenticationEntryPoint(restAuthenticationEntryPoint())
                .and()
                // 将自定义的JWT过滤器放到过滤链中
                .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)
                // 打开Spring Security的跨域
                .cors()
                .and()
                // 关闭CSRF
                .csrf().disable()
                // 关闭Session机制
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

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

    @Bean
    public RestfulAccessDeniedHandler restfulAccessDeniedHandler() {
        return new RestfulAccessDeniedHandler();
    }

    @Bean
    public RestAuthenticationEntryPoint restAuthenticationEntryPoint() {
        return new RestAuthenticationEntryPoint();
    }

    
    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
        return new JwtAuthenticationTokenFilter();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Bean
    public AccessDecisionVoter accessDecisionProcessor() {
        return new AccessDecisionProcessor();
    }

    @Bean
    public AccessDecisionManager accessDecisionManager() {
        // 构造一个新的AccessDecisionManager 放入两个投票器
        List> decisionVoters = Arrays.asList(new WebexpressionVoter(), accessDecisionProcessor());
        return new Unanimousbased(decisionVoters);
    }

    @Bean
    public IgnoreUrlsConfig ignoreUrlsConfig() {
        return new IgnoreUrlsConfig();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider);
    }
}
AccessDecisionProcessor
public class AccessDecisionProcessor implements AccessDecisionVoter {

    
    @Override
    public int vote(Authentication authentication, FilterInvocation object, Collection attributes) {
        assert authentication != null;
        assert object != null;

        // 拿到当前请求uri
        String requestUrl = object.getRequestUrl();
        String method = object.getRequest().getMethod();
        log.debug("进入自定义鉴权投票器,URI : {} {}", method, requestUrl);

        String key = requestUrl + ":" + method;
        return ACCESS_GRANTED;
        // 如果没有缓存中没有此权限也就是未保护此API,弃权
        PermissionInfoBO permission = caffeineCache.get(CacheName.PERMISSION, key, PermissionInfoBO.class);
        if (permission == null) {
            return ACCESS_ABSTAIN;
        }

        // 拿到当前用户所具有的权限
        List roles = ((UserDetail) authentication.getPrincipal()).getRoles();
        if (roles.contains(permission.getRoleCode())) {
            return ACCESS_GRANTED;
        } else {
          return ACCESS_DENIED;
        }
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class clazz) {
        return true;
    }
}
RestfulAccessDeniedHandler
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request,
                       HttpServletResponse response,
                       AccessDeniedException e) throws IOException, ServletException {

        response.setHeader("Cache-Control", "no-cache");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.getWriter().println(JSONObject.toJSONString(StandardResultVO.forbiddenResult()));
        response.getWriter().flush();
    }
}
RestAuthenticationEntryPoint
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setHeader("Cache-Control", "no-cache");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.getWriter().println(JSONObject.toJSONString(StandardResultVO.errorResult(401, authException.getMessage())));
        response.getWriter().flush();
    }
}
JwtAuthenticationTokenFilter
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private JwtProvider jwtProvider;
    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private CustomUserDetailsService userDetailsService;

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

        // 拿到Authorization请求头内的信息
        String authToken = jwtProvider.getToken(request);

        // 判断一下内容是否为空
        if (StrUtil.isNotEmpty(authToken) && authToken.startsWith(jwtProperties.getTokenPrefix())) {
            // 去掉token前缀,拿到真实token
            authToken = authToken.substring(jwtProperties.getTokenPrefix().length());

            // 拿到token里面的登录账号
            String loginAccount = jwtProvider.getSubjectFromToken(authToken);

            if (StrUtil.isNotEmpty(loginAccount) && SecurityContextHolder.getContext().getAuthentication() == null) {
                // 查询用户
                UserDetails userDetail = userDetailsService.loadUserByUsername(loginAccount);

                // 拿到用户信息后验证用户信息与token
                if (userDetail != null && jwtProvider.validateToken(authToken, userDetail)) {

                    // 组装authentication对象,构造参数是Principal Credentials 与 Authorities
                    // 后面的拦截器里面会用到 grantedAuthorities 方法
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetail, userDetail.getPassword(), userDetail.getAuthorities());

                    // 将authentication信息放入到上下文对象中
                    SecurityContextHolder.getContext().setAuthentication(authentication);

                    log.info("JWT过滤器通过校验请求头token自动登录成功, user : {}", userDetail.getUsername());
                }
            }
        }

        chain.doFilter(request, response);
    }
}
IgnoreUrlsConfig
@Getter
@Setter
@ConfigurationProperties(prefix = "secure.ignored")
public class IgnoreUrlsConfig {

    private List urls = new ArrayList<>();

}
CustomAuthenticationProvider
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String name = authentication.getName();
        String passwd = (String) authentication.getCredentials();
        UserDetails userDetails = userDetailsService.loadUserByUsername(name);
        if (Objects.isNull(userDetails) || StrUtil.isEmpty(userDetails.getUsername())) {
            throw new BadCredentialsException("用户不存在");
        }
        boolean verify = MD5Util.verify(passwd, userDetails.getPassword());
        if (verify) {
            UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials(), userDetails.getAuthorities());
            result.setDetails(authentication.getDetails());
            return result;
        }
        throw new BizException("用户名或密码错误,请重新输入。");
    }

    @Override
    public boolean supports(Class aClass) {
        return true;
    }
}
CustomUserDetailsService
@Slf4j
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private AccountService accountService;

    @Autowired
    private UserRoleService userRoleService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.debug("开始登陆验证,用户名为: {}", username);

        // 根据用户名验证用户
        UserInfo userInfo = null;
        if (RegexUtil.isMobile(username)) {
            log.debug("手机号登陆:{}", username);
            userInfo = accountService.getUserInfoByPhone(username);
        } else {
            log.debug("用户名或者账号登陆:{}", userInfo);
            userInfo = accountService.getUserInfoByNameOrAccount(username);
        }

        if (userInfo == null) {
            throw new BizException("用户名或密码错误,请重新输入。");
        }

        // 构建UserDetail对象
        UserDetail userDetail = new UserDetail();
        userDetail.setUserInfo(userInfo);
        List roles = userRoleService.getRolesByUserId(userInfo.getUserId());
        userDetail.setRoleInfoList(roles);
        return userDetail;
    }
}
JwtProvider
@Slf4j
@Component
public class JwtProvider {

    @Autowired
    private JwtProperties jwtProperties;

    
    public String getToken(HttpServletRequest request) {
        return request.getHeader(jwtProperties.getRequestHeader());
    }

    
    public AccessToken createToken(UserDetails userDetails) {
        return createToken(userDetails.getUsername());
    }

    
    public AccessToken createToken(String subject) {
        // 当前时间
        final Date now = new Date();
        // 过期时间
        final Date expirationDate = new Date(now.getTime() + jwtProperties.getExpirationTime() * 1000);

        String token = jwtProperties.getTokenPrefix() + Jwts.builder()
                .setSubject(subject)
                .setIssuedAt(now)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, jwtProperties.getApiSecretKey())
                .compact();
        return AccessToken.builder().loginAccount(subject).token(token).expirationTime(expirationDate).build();
    }

    
    public boolean validateToken(String token, UserDetails userDetails) {
        Claims claims = getClaimsFromToken(token);
        return claims.getSubject().equals(userDetails.getUsername()) && !isTokenExpired(claims);
    }


    
    public AccessToken refreshToken(String oldToken) {
        String token = oldToken.substring(jwtProperties.getTokenPrefix().length());

        // token反解析
        Claims claims = getClaimsFromToken(token);

        //如果token在15分钟之内刚刷新过,返回原token
        if (tokenRefreshJustBefore(claims)) {
            return AccessToken.builder().loginAccount(claims.getSubject()).token(oldToken).expirationTime(claims.getExpiration()).build();
        } else {
            return createToken(claims.getSubject());
        }
    }

    
    private boolean tokenRefreshJustBefore(Claims claims) {
        Date refreshDate = new Date();
        //刷新时间在创建时间的指定时间内
        if (refreshDate.after(claims.getExpiration()) && refreshDate.before(DateUtil.offsetSecond(claims.getExpiration(), 900))) {
            return true;
        }
        return false;
    }

    
    private Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(jwtProperties.getApiSecretKey())
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            log.error("JWT反解析失败, token已过期或不正确, token : {}", token);
        }
        return claims;
    }


    
    public String getSubjectFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        if (claims != null) {
            return claims.getSubject();
        } else {
            return null;
        }
    }


    
    private boolean isTokenExpired(Claims claims) {
        return claims.getExpiration().before(new Date());
    }


}
UserContext
public class UserContext {

    
    public static UserDetail getCurrentUser() {
        return (UserDetail)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }

    
    public static String getUsername(){
        return getCurrentUser().getUsername();
    }

    
    public static String getUserId(){
        return getCurrentUser().getUserId();
    }
}
UserDetail
@Data
public class UserDetail implements Serializable, UserDetails {
    private static final long serialVersionUID = 1L;

    private UserInfo userInfo;
    private List roleInfoList;
    private Collection grantedAuthorities;
    private List roles;

    public String getUserId() {
        return this.userInfo.getUserId();
    }

    public UserInfo getUserInfo() {
        return userInfo;
    }

    @Override
    public Collection getAuthorities() {
        if (grantedAuthorities != null) {
            return this.grantedAuthorities;
        }
        List grantedAuthorities = new ArrayList<>();
        List authorities = new ArrayList<>();
        roleInfoList.forEach(role -> {
            authorities.add(role.getRoleId());
            grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleId()));
        });
        this.grantedAuthorities = grantedAuthorities;
        this.roles = authorities;
        return this.grantedAuthorities;
    }

    @Override
    public String getPassword() {
        return this.userInfo.getPassword();
    }

    @Override
    public String getUsername() {
        return this.userInfo.getAccount();
    }

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

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

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

    
    @Override
    public boolean isEnabled() {
        return userInfo.getDeleted() == 0;
    }
}
具体流程

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/351742.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号