spring security登入和鉴权

1. User

@Entity
@Table(name = "user")
public class User implements UserDetails{

    
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)// 自增长策略
    private Integer id;
    
    @Column
    private String username;
    
    @Column
    @JsonIgnore
    private String password;
    
    @Column
    private String phone;

    // JPA 的规范要求无参构造函数;设为 protected 防止直接使用
    protected User() {
        
    }
    
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    
    
    
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // TODO Auto-generated method stub
        return null;
    }
    
    @Override
    @JsonIgnore
    public boolean isAccountNonExpired() {
        
        return true;
    }

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

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

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

 

2. static类

public class SecurityConstants {

    public final static String AUTHORIZATION = "Authorization";
    
    public final static String AUTHORIZATION_PRE = "Bearer";
    
    public final static String AUTH_SIGNING_KEY = "MySecret";
    
    
    public final static Integer SC_OK = 200;
    public final static Integer SC_ERROR_USER_OR_PASS = 201;  //用户名或密码错误
    public final static Integer SC_JWT_EXPIRED = 202;  //token过期
    public final static Integer SC_JWT_NO_AUTH = 203;  //用户无权限
    public final static Integer SC_JWT_NO_TOKEN = 204;  //缺少token
    public final static Integer SC_JWT_INVALID_TOKEN = 205; //无效token(username为空)
    public final static Integer SC_JWT_MALFORMED = 206;  //token格式错误
    public final static Integer SC_JWT_ERROR_SIGNATURE = 207;  //token不匹配
}

 

3. 配置WebSecurityConfigurerAdapter

   extends

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

       @Autowired

       private UserDetailsService udService;

       @Autowired

       private JupiterLogoutSuccessHandler jLogoutSuccessHandler;

}

   override

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
    
        auth.userDetailsService(udService).passwordEncoder(new BCryptPasswordEncoder());
        
    }

   override

    @Override
    protected void configure(HttpSecurity http) throws Exception {
         
        http.cors()//允许跨域,在filter中也设置了,这里应该可以不设置
            .and().csrf().disable()//csrf针对的是session的攻击,使用jwt就不需要这个保护
            // 基于token,所以不需要session
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and().logout().logoutUrl("/logout").logoutSuccessHandler(jLogoutSuccessHandler)
            .and()
            .addFilter(new JWTLoginFilter(super.authenticationManager()))
            .addFilter(new JWTAuthenticationFilter(super.authenticationManager()));

              //.and().authorizeRequests().antMatchers(HttpMethod.GET,"/**").permitAll()//这样配置无效,还是web.ignoring可以生效

              //.antMatchers("/user/signup","/h2db/**").permitAll()

    }

override

    /**
     * 配置需要放行的接口
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        
        web.ignoring().antMatchers(HttpMethod.GET,"/**")
                      .antMatchers("/user/signup","/h2db/**");
    }

 

4. 配置登入UsernamePasswordAuthenticationFilter

   extends

public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter{

private AuthenticationManager authenticationManager;

      public JWTLoginFilter() {

    }
    public JWTLoginFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
}
}

   Override

    @Override
    public Authentication attemptAuthentication(HttpServletRequest req,
                                                HttpServletResponse res) throws AuthenticationException {try {
            
            String s = IOUtils.toString(req.getInputStream(), StandardCharsets.UTF_8);
            JSONObject j = JSON.parseObject(s);
            String username = j.getString("username");
            String psw = j.getString("password");
            
            UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username.trim(),psw);
          
            return authenticationManager.authenticate(authToken);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

   Override

    /**
     * 用户名密码匹配成功后,这个方法会被调用,生成token
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, 
            FilterChain chain, Authentication auth) throws IOException, ServletException {
         User user = (User)auth.getPrincipal();//生成token
        Map<String,Object> sub = new HashMap<String,Object>();
        sub.put("username", user.getUsername());//在token中保存id方便后续使用//
        String token = Jwts.builder()
                .setSubject(JsonUtil.ObjectToJson(sub))
                .setExpiration(new Date(System.currentTimeMillis() + 60 * 1000 * 60 * 24))//24h: 60 * 1000 * 60 * 24
                .signWith(SignatureAlgorithm.HS512, SecurityConstants.AUTH_SIGNING_KEY)
                .compact();
    
        res.setStatus(SecurityConstants.SC_OK);
        
        //允许前端获取自定义的header
        res.setHeader("Access-Control-Expose-Headers", "Authorization,username");//,rolename,department
        //装载自定义key(前端需要decode)
        String name = URLEncoder.encode(user.getUsername(), "UTF-8").replaceAll("\\+", "%20");
        //
        res.addHeader(SecurityConstants.AUTHORIZATION, SecurityConstants.AUTHORIZATION_PRE + token);
        res.addHeader("username", name);
    
    }

   Override

    /**
     * 用户名不存在时,虽然在UserDetailsServiceImpl中抛出了UsernameNotFoundException
     * 但被别的方法捕获并且重新抛出BadCredentialsException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request,HttpServletResponse response, 
            AuthenticationException failed) throws IOException, ServletException {if(failed instanceof UsernameNotFoundException) {
            log.info("---unsuccessfulAuthentication---- is UsernameNotFound");    
        }
        log.info("--JWTLoginFilter-unsuccessfulAuthentication----"+failed.getLocalizedMessage());
        log.info("--JWTLoginFilter-unsuccessfulAuthentication----"+failed.getMessage());
        log.info("--JWTLoginFilter-unsuccessfulAuthentication----"+failed.getCause());
        log.info("--JWTLoginFilter-unsuccessfulAuthentication----"+failed.getStackTrace());
        
        response.setStatus(SecurityConstants.SC_ERROR_USER_OR_PASS);
    }

 

   implements

@Service
public class UserDetailsServiceImpl implements UserDetailsService{
    
    @Autowired
    private UserRepository userRep;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        
        User user = userRep.findByUsername(username);if(StringUtil.isNull(user)) {throw new UsernameNotFoundException("username NOT found");
        }
        return user;
    }
}

 

5. 鉴权类BasicAuthenticationFilter

public class JWTAuthenticationFilter extends BasicAuthenticationFilter{
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }
    
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        
        String token = request.getHeader(SecurityConstants.AUTHORIZATION);//不区分大小写
 
        if (StringUtil.isNull(token) || !token.startsWith(SecurityConstants.AUTHORIZATION_PRE)) {
            response.setStatus(SecurityConstants.SC_JWT_NO_TOKEN);
            return;
        }
        //验证token
        try {

            Jwts.parser().setSigningKey(SecurityConstants.AUTH_SIGNING_KEY)
                        .parseClaimsJws(token.replace(SecurityConstants.AUTHORIZATION_PRE, ""))
                        .getBody().getSubject();
//如果需要获取sub,再从sub中获取之前设置的字典
//String sub= .getSubject(); }
catch(ExpiredJwtException e) { response.setStatus(SecurityConstants.SC_JWT_EXPIRED);//过了期限 return; }catch(MalformedJwtException e) { response.setStatus(SecurityConstants.SC_JWT_MALFORMED);//token格式错误 return; }catch(SignatureException e) { response.setStatus(SecurityConstants.SC_JWT_ERROR_SIGNATURE);//token不匹配 return; } //继续其他的filter chain.doFilter(request, response); } }

6. 登出类

@Component
public class JupiterLogoutSuccessHandler implements LogoutSuccessHandler{

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {
      
    }
}

 

posted @ 2022-05-24 10:26  jason47  阅读(269)  评论(0编辑  收藏  举报