文档章节

SpringSecurity 自定义登陆方式验证user身份发布jwt令牌

edison_kwok
 edison_kwok
发布于 01/19 22:55
字数 1594
阅读 323
收藏 0

这里我们已账户密码登录为例

public ResponseEntity<MsgInfo<UserVM>> authenticate(@Valid @RequestBody LoginVM loginVM) {  
    Map<String, String> objects = new HashMap<>();  
    objects.put("password", loginVM.getPassword());  
    final UsernamePasswordAuthenticationToken authenticationToken =  
        new UsernamePasswordAuthenticationToken(loginVM.getUsername(),  
            objects);  
    final Authentication authentication = this.authenticationManager.authenticate(authenticationToken);  
    SecurityContextHolder.getContext().setAuthentication(authentication);  
    final boolean rememberMe = (loginVM.getRememberMe() == null) ? false : loginVM.getRememberMe();  
    final String jwt = tokenProvider.createToken(authentication, rememberMe);  
  
    final UserVM user = userService.getUserWithAuthoritiesByLogin(loginVM.getUsername());  
    if (!loginStatus.equals(user.getStatus())) {  
        throw new RuntimeException("您的账号已被冻结,暂时不能登录.");  
    }  
    final HttpHeaders httpHeaders = new HttpHeaders();  
    httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " \+ jwt);  
    userLoginLogService.insertOne(user);  
    return new ResponseEntity<>(new MsgInfo(StatusCode.ok, user), httpHeaders, HttpStatus.OK);  
}

一、继承org.springframework.security.authentication.AbstractAuthenticationToken,重写部分方法

public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {  
  
    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL\_VERSION\_UID;  
  
    private Object principal;  
    private Map<String, String> credentials;  
    private Map<String, String> details;  
  
  
    @Override  
  public Object getDetails() {  
        return this.details;  
    }  
  
    public UsernamePasswordAuthenticationToken(Object principal, Map<String, String> credentials) {  
        super(null);  
        this.principal = principal;  
        this.credentials = credentials;  
        setAuthenticated(false);  
    }  
  
    public UsernamePasswordAuthenticationToken(Object principal,  
                                         Map<String, String> credentials,  
                                         Collection<? extends GrantedAuthority> authorities,  
                                         Map<String,String> details) {  
        super(authorities);  
        this.principal = principal;  
        this.credentials = credentials;  
        this.details=details;  
        super.setAuthenticated(true); // must use super, as we override  
  }  
  
    public UsernamePasswordAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {  
        super(authorities);  
        this.principal = principal;  
        this.credentials = null;  
        super.setAuthenticated(true); // must use super, as we override  
  }  
  
    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {  
        if (isAuthenticated) {  
            throw new IllegalArgumentException(  
                "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");  
        }  
  
        super.setAuthenticated(false);  
    }  
  
    @Override  
  public void eraseCredentials() {  
        super.eraseCredentials();  
        credentials = null;  
    }  
  
  
// public MobileCodeAuthenticationToken(Collection<? extends GrantedAuthority> authorities) {  
//    super(authorities);  
//        this.principal = principal;  
//        this.credentials = null;  
//        super.setAuthenticated(true); // must use super, as we override  
// }  
  
  @Override  
  public Object getCredentials() {  
        // TODO Auto-generated method stub  
  return this.credentials;  
    }  
  
    @Override  
  public Object getPrincipal() {  
        // TODO Auto-generated method stub  
  return this.principal;  
    }  
}

这里相当于一个缓存区域,可以缓存用户信息,其中details是很重要的一个变量。在授权过程中其他信息会被清除,但是details属性回保留下来。

二、实现org.springframework.security.authentication.AuthenticationProvider,编写一个第一步写的token的授权提供者

@Component  
public class UsernamePasswordAuthenticationProvider implements AuthenticationProvider {  
  
    @Autowired  
  private UserService userService;  
  
    @Autowired  
  private PasswordEncoder passwordEncoder;  
  
    @Autowired  
  private AuthorityMapper authorityMapper;  
  
    @Override  
 @SuppressWarnings("unchecked")  
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {  
        String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();  
        Map<String, String> params = (Map<String, String>) authentication.getCredentials();  
        String password = params.get("password");  
        if (StringUtils.isEmpty(password)) {  
            throw new BadCredentialsException("密码不能为空");  
        }  
        User user = userService.getUserByAccount(username);  
        if (null == user) {  
            throw new BadCredentialsException("用户不存在");  
        }  
        if (!passwordEncoder.matches(password, user.getPassword())) {  
            throw new BadCredentialsException("用户名或密码不正确");  
        }  
        Map<String, String> details = new HashMap<>(4);  
        details.put("loginType", "password");  
        details.put("login", user.getLogin());  
        details.put("telephone", user.getTelephone());  
        details.put("email", user.getEmail());  
  
        UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(  
            username,  
            params,  
            listUserGrantedAuthorities(user.getLogin()),  
            details);  
        result.setDetails(authentication.getDetails());  
        return result;  
    }  
  
    @Override  
  public boolean supports(Class<?> authentication) {  
        System.out.println(this.getClass().getName() + "---supports");  
        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));  
    }  
  
    private Set<GrantedAuthority> listUserGrantedAuthorities(String login) {  
        Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();  
        if (StringUtils.isEmpty(login)) {  
            return authorities;  
        }  
        Set<Authority> authority = authorityMapper.selectByUserLogin(login);  
        for (Authority auth : authority) {  
            authorities.add(new SimpleGrantedAuthority(auth.getName()));  
        }  
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));  
        return authorities;  
    }  
  
}

public Authentication authenticate(Authentication authentication)这个方法是springSecurity框架在校验授权的过程中会去调用校验的,经过这个authentication()方法之后就是已经经过了校验了。

public boolean supports(Class<?> authentication) 这个方法需要你把第一步的token和该provider进行匹配。

三、颁发jwt令牌

public String createToken(Authentication authentication, boolean rememberMe) {  
    String authorities = authentication.getAuthorities().stream()  
        .map(GrantedAuthority::getAuthority)  
        .collect(Collectors.joining(","));  
    return this.createToken(authentication.getName(), authentication.getDetails(), authorities, rememberMe);  
}  
  
@SuppressWarnings("unchecked")  
public String createToken(String loginName, Object details, String authorities, boolean rememberMe) {  
    long now = (new Date()).getTime();  
    Date validity;  
    if (rememberMe) {  
        validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe \+ this.tokenValidityInMilliseconds);  
    } else {  
        validity = new Date(now + this.tokenValidityInMilliseconds \* 2);  
    }  
    Map<String, String> params;  
    if (details != null) {  
        params = (Map<String, String>) details;  
    } else {  
        params = new HashMap<>(0);  
    }  
    return Jwts.builder()  
        .setSubject(loginName)  
        .claim(AUTHORITIES_KEY, authorities)  
        .claim("telephone", params.get("telephone"))  
        .claim("email", params.get("email"))  
        .signWith(key, SignatureAlgorithm.HS512)  
        .setExpiration(validity)  
        .compact();  
}

这是一个示范,header里面是加密的方式,payload是其中的一些信息。 所以自定义的一些需要存储到token里面的可以用claim(key,value)来加入其中。

这里稍后博客会说明jwt令牌的构成。

四、客户带着token访问时,在过滤器(Filter)中编写过滤器(分布式就要用zuul网关)

继承org.springframework.web.filter.GenericFilterBean,实现过滤器

public class JWTFilter extends GenericFilterBean {  
  
    public static final String AUTHORIZATION_HEADER = "Authorization";  
  
    private TokenProvider tokenProvider;  
  
    public JWTFilter(TokenProvider tokenProvider) {  
        this.tokenProvider = tokenProvider;  
    }  
  
    @Override  
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)  
        throws IOException, ServletException {  
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;  
        String jwt = resolveToken(httpServletRequest);  
        if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {  
            Authentication authentication = this.tokenProvider.getAuthentication(jwt);  
            SecurityContextHolder.getContext().setAuthentication(authentication);  
        }  
        filterChain.doFilter(servletRequest, servletResponse);  
    }  
  
    private String resolveToken(HttpServletRequest request){  
        String bearerToken = request.getHeader(AUTHORIZATION_HEADER);  
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {  
            return bearerToken.substring(7, bearerToken.length());  
        }  
        return null;  
    }  
}

其中包含解析token的,确认token是不是系统办法的

public boolean validateToken(String authToken) {  
    try {  
        Jwts.parser().setSigningKey(key).parseClaimsJws(authToken);  
        return true;  
    } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {  
        log.info("Invalid JWT signature.");  
        log.trace("Invalid JWT signature trace: {}", e);  
    } catch (ExpiredJwtException e) {  
        log.info("Expired JWT token.");  
        log.trace("Expired JWT token trace: {}", e);  
    } catch (UnsupportedJwtException e) {  
        log.info("Unsupported JWT token.");  
        log.trace("Unsupported JWT token trace: {}", e);  
    } catch (IllegalArgumentException e) {  
        log.info("JWT token compact of handler are invalid.");  
        log.trace("JWT token compact of handler are invalid trace: {}", e);  
    }  
    return false;  
}

直接解析token来进行获取到自己编写的token类

public Authentication getAuthentication(String token) {  
    Claims claims = Jwts.parser()  
        .setSigningKey(key)  
        .parseClaimsJws(token)  
        .getBody();  
  
    //在Token过期时间到期后,并在另一个ExpireationTime时间范围内,系统会自动刷新Token,并返回前端  
  long now = new Date().getTime() - ((long) claims.getExpiration().getTime() - this.tokenValidityInMilliseconds);  
    boolean refreshed = false;  
    if (0 < now && now < this.tokenValidityInMilliseconds) {  
        token = refreshToken(claims);  
        refreshed = true;  
    }  
  
    Collection<? extends GrantedAuthority> authorities =  
        Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))  
            .map(SimpleGrantedAuthority::new)  
            .collect(Collectors.toList());  
  
    Map<String, String> properties = new HashMap<>(3);  
    properties.put("telephone", String.valueOf(claims.get("telephone")));  
    properties.put("email", String.valueOf(claims.get("email")));  
  
    AuthenticationUser principal = new AuthenticationUser(claims.getSubject(), "", authorities, properties);  
  
    CommonAuthenticationToken authenticationToken = new CommonAuthenticationToken(  
        principal,  
        token,  
        authorities);  
    authenticationToken.setDetails(refreshed);  
    return authenticationToken;  
}

SecurityContextHolder.getContext().setAuthentication(authentication); 就是将该登录客户放入缓存中。

五、进行springSecurity配置适配器进行

写一个继承自org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter的类

@Configuration  
@EnableWebSecurity  //springSecurity自带注解
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)  //使用该配置进行
@Import(SecurityProblemSupport.class)  
@Order(1000)  //放到后面再加载
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {  
  
    private final AuthenticationManagerBuilder authenticationManagerBuilder;  
  
    private final UserDetailsService userDetailsService;  
  
    private final TokenProvider tokenProvider;  
  
    private final CorsFilter corsFilter;  
  
    private final SecurityProblemSupport problemSupport;  
  
    @Autowired  
 @Qualifier("authenticationManagerBean")  
    private AuthenticationManager authenticationManager;  
  
    @Autowired  //用户名和密码登录
  private UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider;  
    @Autowired  //验证码登录
  private MobileCodeAuthenticationProvider mobileCodeAuthenticationProvider;  
  
  
    @Override  
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {  
        auth  //这里是将我们写好的provider注入到springSecurity框架中
		//该地放provider的顺序是框架springSecurity调用authentication()的顺序
		//只要有一个provider就可以通过就可以,全部不通过就算是全部失败,验证失败
                .authenticationProvider(mobileCodeAuthenticationProvider)  
                .authenticationProvider(usernamePasswordAuthenticationProvider)  
                ;  
    }  
  
  
    public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService, TokenProvider tokenProvider, CorsFilter corsFilter, SecurityProblemSupport problemSupport) {  
        this.authenticationManagerBuilder = authenticationManagerBuilder;  
        this.userDetailsService = userDetailsService;  
        this.tokenProvider = tokenProvider;  
        this.corsFilter = corsFilter;  
        this.problemSupport = problemSupport;  
    }  
  
    @PostConstruct  
  public void init() {  
        try {  
            DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();  
            authenticationProvider.setHideUserNotFoundExceptions(false);  
            authenticationProvider.setUserDetailsService(userDetailsService);  
            authenticationProvider.setPasswordEncoder(passwordEncoder());  
            authenticationManagerBuilder  
  .authenticationProvider(authenticationProvider);  
        } catch (Exception e) {  
            throw new BeanInitializationException("Security configuration failed", e);  
        }  
    }  
  
    @Override  
 @Bean  public AuthenticationManager authenticationManagerBean() throws Exception {  
        AuthenticationManager authenticationManager = super.authenticationManagerBean();  
        return authenticationManager;  
    }  
  
    @Bean  //这个是密码加密器,不可逆转
  public PasswordEncoder passwordEncoder() {  
        return new BCryptPasswordEncoder();  
    }  
  
    @Override  
  public void configure(WebSecurity web) throws Exception {  
        web.ignoring()  
                .antMatchers(HttpMethod.OPTIONS, "/**")  
                .antMatchers("/app/**/*.{js,html}")  
                .antMatchers("/i18n/**")  
                .antMatchers("/content/**")  
                .antMatchers("/swagger-ui/index.html")  
                .antMatchers("/test/**");  
    }  
  
    @Override  
  public void configure(HttpSecurity http1) throws Exception {  
        ExceptionHandlingConfigurer<HttpSecurity> http = http1  
                .csrf()  
                .disable()  
                .addFilterBefore(bhAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)  
                .exceptionHandling()  
                .authenticationEntryPoint(problemSupport)  
                .accessDeniedHandler(problemSupport);  
        http.and()  
                .headers()  
                .frameOptions()  
                .disable();  
        http.and()  
                .sessionManagement()  
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);  
        http.and()  
                .authorizeRequests()  
                .antMatchers("/api/register").permitAll()  
                .antMatchers("/api/activate").permitAll()  
                .antMatchers("/api/authenticate").permitAll()  
                .antMatchers("/api/account/reset-password/init").permitAll()  
                .antMatchers("/api/account/reset-password/finish").permitAll()  
                .antMatchers("/management/health").permitAll()  
                .antMatchers("/management/info").permitAll()  
                .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN);  
        http.and()  
                .authorizeRequests()  
                .antMatchers("/api/**").permitAll();        //关闭权限验证  
 //.antMatchers("/api/**").authenticated(); //打开权限验证  http.and()  
                .apply(securityConfigurerAdapter());  
  
    }  
  
    @Bean  
  public BhAuthenticationFilter bhAuthenticationFilter() {  
        BhAuthenticationFilter filter = new BhAuthenticationFilter();  
        filter.setAuthenticationManager(authenticationManager);  
        return filter;  
    }  
  
  
    private JWTConfigurer securityConfigurerAdapter() {  
        return new JWTConfigurer(tokenProvider);  
    }  
}

到此,springSecurity已经完成jwt令牌的颁发和校验。

© 著作权归作者所有

edison_kwok
粉丝 7
博文 177
码字总数 159055
作品 0
成都
程序员
私信 提问
加载中

评论(0)

编程界的小学生/common-security

common-security 目前包含如下几大模块: common-security-corecommon-security-validatecommon-security-browsercommon-security-app 一、common-security-validate 1、是什么? 通用的验证......

编程界的小学生
2018/07/03
0
0
Spring Security 5.0.0.M3 发布,Spring 安全框架

Spring Security 5.0.0.M3 发布了。Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架。 Spring Security 为基于J2EE企业应用软件提供了全面安全服务...

淡漠悠然
2017/07/25
1.3K
4
spring boot ,spring security如何禁用session

spring boot 2.0.2项目中使用spring security和 JWT进行登录认证, 在spring security配置中禁用session 请求接口后,浏览器的cookie中还是有JSESSIONID, spring boot 内置的Tomcat中还是创...

bithup
2018/06/21
8.4K
3
测试和破坏 JWT 认证的几种实用方法

      引言   JWT (JSON Web Token)是一种流行的身份验证 / 授权协议。它将加密签名集成到 JSON 对象中,以验证对象的完整性。   测试 JWT 安全性的方法比较系统的也相当简单。目前,...

嘶吼RoarTalk
01/04
0
0
ApiBoot 2.0.5.RELEASE 版本发布

本次更新内容 ApiBoot Message Push(推送服务集成) - 极光推送组件(全平台、安卓平台、IOS线上、线下平台)初版发布 ApiBoot Resource Load (资源业务自动化分离) - 添加资源Redis缓存支持 ...

恒宇少年
2019/04/23
1.9K
5

没有更多内容

加载失败,请刷新页面

加载更多

深圳哪里有开加油费发票-深圳新闻网

深圳哪里有开加油费发票【電徴同步;135 * 64 99 * 81 96】陈生,诚、信、合、作,保、真、售、后、保、障、长、期、有、效。adb的全称为Android Debug B...

票微fp2090
13分钟前
18
0
南宁哪里可以开软件开发发票-腾讯新闻网

南宁哪里可以开软件开发发票【152 * 9б 28 * 21 б9】陈生,诚、信、合、作,保、真、售、后、保、障、长、期、有、效。adb的全称为Android Debug Bri...

17035270196
14分钟前
39
0
南宁哪里可以开电线电缆发票-腾讯新闻网

南宁哪里可以开电线电缆发票【152 * 9б 28 * 21 б9】陈生,诚、信、合、作,保、真、售、后、保、障、长、期、有、效。adb的全称为Android Debug Bri...

17035270061
15分钟前
25
0
南宁哪里可以开手撕定额发票-腾讯新闻网

南宁哪里可以开手撕定额发票【152 * 9б 28 * 21 б9】陈生,诚、信、合、作,保、真、售、后、保、障、长、期、有、效。adb的全称为Android Debug Bri...

15232501324
15分钟前
33
0
南宁哪里可以开礼品费发票-腾讯新闻网

南宁哪里可以开礼品费发票【152 * 9б 28 * 21 б9】陈生,诚、信、合、作,保、真、售、后、保、障、长、期、有、效。adb的全称为Android Debug Bridg...

17035270092
17分钟前
15
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部