生活随笔
收集整理的這篇文章主要介紹了
Spring Security 初体验
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Spring Security 初體驗
①認證過濾器(登錄)
用于接收前端用戶登錄信息(username和password)與數據庫用戶信息(通過UserDetailsService查詢)就行判斷。
UserDetailsService:查詢存在用戶信息返回SecurityUser對象,否則拋出異常。
JWTPasswordHandler:判斷密碼是否正確。
@Slf4j
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {private final AuthenticationManager authenticationManager
;private RedisTemplate redisTemplate
;public JWTAuthenticationFilter(AuthenticationManager authenticationManager
, RedisTemplate redisTemplate
) {this.authenticationManager
= authenticationManager
;this.redisTemplate
= redisTemplate
;this.setPostOnly(false);this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/member/rbac/login", "POST"));}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request
, HttpServletResponse response
) throws AuthenticationException {try {User user
= new ObjectMapper().readValue(request
.getInputStream(), User.class);return authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(user
.getUsername(), user
.getPassword()));} catch (IOException e
) {throw new RuntimeException(e
);}}@Overrideprotected void successfulAuthentication(HttpServletRequest request
, HttpServletResponse response
, FilterChain chain
, Authentication authResult
) throws IOException, ServletException {SecurityUser securityUser
= (SecurityUser) authResult
.getPrincipal();String token
= JWTTokenHandler.createToken(securityUser
.getCurrentUserInfo().getId(), securityUser
.getCurrentUserInfo().getUsername(), securityUser
.getPermissionValueList());log
.info("login success,Token:" + token
);HashMap<String, String> login
= new HashMap<>();login
.put("token", token
);Result<HashMap<String, String>> ok
= Result.OK(login
);response
.setContentType("text/html;charset=utf-8");response
.getWriter().write(new ObjectMapper().writeValueAsString(ok
));redisTemplate
.opsForValue().set(securityUser
.getCurrentUserInfo().getUsername(), token
);}@Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request
, HttpServletResponse response
, AuthenticationException failed
) throws IOException, ServletException {log
.info("login failure,Error:" + failed
.toString());Result<Object> error
= Result.error("登錄失敗");response
.setContentType("text/html;charset=utf-8");response
.getWriter().write(new ObjectMapper().writeValueAsString(error
));}
}
②授權過濾器(分配角色)
每次請求,判斷是否帶有token,如果存在則根據token得到用戶角色,并在全局安全框架上下文設置用戶角色。不存在則直接進入過濾(沒角色)。
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {public JWTAuthorizationFilter(AuthenticationManager authenticationManager
) {super(authenticationManager
);}@Overrideprotected void doFilterInternal(HttpServletRequest request
,HttpServletResponse response
,FilterChain chain
) throws IOException, ServletException {logger
.info("request uri:" + request
.getRequestURI());String token
= request
.getHeader(SecurityConstants.TOKEN_HEADER
);if (StringUtils.isEmpty(token
)) {logger
.warn("X-Token: not exist!");SecurityContextHolder.clearContext();chain
.doFilter(request
, response
);return;}logger
.info("X-Token:" + token
);SecurityContextHolder.getContext().setAuthentication(getAuthentication(token
));System.out
.println(SecurityContextHolder.getContext());super.doFilterInternal(request
, response
, chain
);}private UsernamePasswordAuthenticationToken getAuthentication(String token
) {try {String username
= JWTTokenHandler.getUsernameByToken(token
);List<String> roles
= JWTTokenHandler.getRolesByToken(token
);if (Objects.requireNonNull(roles
).size() > 0) {List<SimpleGrantedAuthority> authorities
= roles
.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());return new UsernamePasswordAuthenticationToken(username
, token
, authorities
);}} catch (ExpiredJwtException exception
) {logger
.error("Request to parse JWT with invalid signature . Detail : " + exception
.getMessage());}return null;}
}
③實體類
SecurityUser:安全框架實體類
User:前端用戶登錄信息類
@Data
@Slf4j
public class SecurityUser implements UserDetails {private transient User currentUserInfo
;private List<String> permissionValueList
;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic String getPassword() {return currentUserInfo
.getPassword();}@Overridepublic String getUsername() {return currentUserInfo
.getUsername();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}
@Data
@ApiModel(description
= "用戶實體類")
public class User implements Serializable {private String id
;private String username
; private String password
; private String salt
;private String token
;
}
④處理類
JWTLogoutHandler:登出處理類(登出成功后處理)
JWTPasswordHandler:密碼處理類(密碼加密和密碼比較)
JWTTokenHandler:token處理類(生成token和通過token獲取用戶信息)
@Slf4j
public class JWTLogoutHandler implements LogoutSuccessHandler {private RedisTemplate redisTemplate
;public JWTLogoutHandler(RedisTemplate redisTemplate
) {this.redisTemplate
= redisTemplate
;}@Overridepublic void onLogoutSuccess(HttpServletRequest request
, HttpServletResponse response
, Authentication authentication
) throws IOException, ServletException {String token
= request
.getHeader(SecurityConstants.TOKEN_HEADER
);if (token
!= null) {String username
= JWTTokenHandler.getUsernameByToken(token
);redisTemplate
.delete(username
);}log
.info("logout success");Result<String> logout
= Result.OK("logout success");response
.setContentType("text/html;charset=utf-8");try {response
.getWriter().write(new ObjectMapper().writeValueAsString(logout
));} catch (IOException e
) {e
.printStackTrace();}}
}
@Slf4j
public class JWTPasswordHandler implements PasswordEncoder {@Overridepublic String encode(CharSequence originalPassword
) { return SecureUtil.md5(originalPassword
.toString());}@Overridepublic boolean matches(CharSequence originalPassword
, String encodedPassword
) { return originalPassword
.equals(encodedPassword
);}
}
public class JWTTokenHandler {public static String createToken(String id
, String username
, List<String> roles
) {String JwtToken = Jwts.builder().setHeaderParam("typ", "JWT").setHeaderParam("alg", "HS256").setSubject("zqy-user").setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRE
)).claim("id", id
).claim("username", username
).claim("roles", roles
).signWith(SignatureAlgorithm.HS256
, SecurityConstants.JWT_SECRET_KEY
).compact();return JwtToken;}public static String getUsernameByToken(String token
) {if (StringUtils.isEmpty(token
)) return "";Jws<Claims> claimsJws
= Jwts.parser().setSigningKey(SecurityConstants.JWT_SECRET_KEY
).parseClaimsJws(token
);Claims claims
= claimsJws
.getBody();return (String) claims
.get("username");}public static String getUserIdByToken(String jwtToken
) {if (StringUtils.isEmpty(jwtToken
)) return "";Jws<Claims> claimsJws
= Jwts.parser().setSigningKey(SecurityConstants.JWT_SECRET_KEY
).parseClaimsJws(jwtToken
);Claims claims
= claimsJws
.getBody();return (String) claims
.get("id");}public static List<String> getRolesByToken(String jwtToken
) {if (StringUtils.isEmpty(jwtToken
)) return null;Jws<Claims> claimsJws
= Jwts.parser().setSigningKey(SecurityConstants.JWT_SECRET_KEY
).parseClaimsJws(jwtToken
);Claims claims
= claimsJws
.getBody();return (List<String>) claims
.get("roles");}
}
⑤認證詳情服務類(數據庫查詢用戶信息)
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Resourceprivate SysUserService sysUserService
;@Resourceprivate SysRoleService sysRoleService
;@Resourceprivate UserRoleService userRoleService
;@Overridepublic UserDetails loadUserByUsername(String username
) throws UsernameNotFoundException {SysUser sysUser
= sysUserService
.selectByUsername(username
);if (sysUser
== null) {throw new UsernameNotFoundException("用戶不存在!");}User curUser
= new User();BeanUtils.copyProperties(sysUser
, curUser
);List<UserRole> userRoleList
= userRoleService
.getUserRoleByUserId(curUser
.getId());List<String> roleIdList
= userRoleList
.stream().map(UserRole::getRoleId).collect(Collectors.toList());List<SysRole> sysRoles
= sysRoleService
.listByIds(roleIdList
);List<String> authorities
= sysRoles
.stream().map(sysRole
-> "ROLE_" + sysRole
.getRoleCode()).collect(Collectors.toList());SecurityUser securityUser
= new SecurityUser();securityUser
.setCurrentUserInfo(curUser
);securityUser
.setPermissionValueList(authorities
);return securityUser
;}
}
⑥常量類
public final class SecurityConstants {public static final long EXPIRE
= 1000 * 60 * 60 * 24; public static final long EXPIRATION
= 60 * 60L;public static final long EXPIRATION_REMEMBER
= 60 * 60 * 24 * 7L;public static final String JWT_SECRET_KEY
= "C*F-JaNdRgUkXn2r5u8x/A?D(G+KbPeShVmYq3s6v9y$B&E)H@McQfTjWnZr4u7w";public static final String TOKEN_HEADER
= "X-Token";public static final String[] RESOURCE_WHITELIST
= {"/swagger-ui.html","/swagger-ui/*","/swagger-resources/**","/v2/api-docs","/v3/api-docs","/webjars/**","/doc.html",};public static final String H2_CONSOLE
= "/h2-console/**";public static final String[] AUTHENTICATION_WHITELIST
= {"/member/rbac/login","/member/rbac/logout","/member/rbac/register",};public static final String[] AUTHORIZATION_WHITELIST
= {};private SecurityConstants() {}}
⑦核心配置類(將上面自定義的類進行配置)
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled
= true)
@Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Resourceprivate UserDetailsService userDetailsService
;@Resourceprivate RedisTemplate redisTemplate
;@Overrideprotected void configure(HttpSecurity http
) throws Exception {log
.info("spring-security started successfully!");http
.csrf().disable().authorizeRequests().antMatchers(SecurityConstants.AUTHENTICATION_WHITELIST
).anonymous().antMatchers(SecurityConstants.AUTHORIZATION_WHITELIST
).permitAll().anyRequest().authenticated().and().logout().logoutUrl("/member/rbac/logout").logoutSuccessHandler(new JWTLogoutHandler(redisTemplate
)).and().addFilter(new JWTAuthenticationFilter(authenticationManager(), redisTemplate
)).addFilter(new JWTAuthorizationFilter(authenticationManager())).sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS
).and().exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint()).accessDeniedHandler(new JWTAccessDeniedHandler());}@Overridepublic void configure(AuthenticationManagerBuilder auth
) throws Exception {auth
.userDetailsService(userDetailsService
).passwordEncoder(new JWTPasswordHandler());}@Overridepublic void configure(WebSecurity web
) throws Exception {web
.ignoring().antMatchers(SecurityConstants.RESOURCE_WHITELIST
);}
}
總結
以上是生活随笔為你收集整理的Spring Security 初体验的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。