参考文献
基于SpringBoot2.2.2.RELEASE
依赖
1 2 3 4 // springboot_version= '2.2.2.RELEASE' implementation "org.springframework.boot:spring-boot-starter:$springboot_version" implementation "org.springframework.boot:spring-boot-starter-web:$springboot_version" implementation "org.springframework.boot:spring-boot-starter-security:$springboot_version"
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) class WebSecurityConfig : WebSecurityConfigurerAdapter () { @Autowired private val userDetailsService: UserDetailsService? = null @Throws(Exception::class) override fun configure (auth: AuthenticationManagerBuilder ) { auth.authenticationProvider(JwtAuthenticationProvider(userDetailsService)) } override fun configure (web: WebSecurity ?) { super .configure(web) } override fun configure (http: HttpSecurity ) { http.cors().and().csrf().disable() .authorizeRequests() .antMatchers(HttpMethod.OPTIONS, "/**" ).permitAll() .antMatchers("/login" ).permitAll() .antMatchers("/user/login" ).permitAll() .antMatchers("/user/create-user" ).permitAll() .anyRequest().authenticated(); http.logout().logoutSuccessHandler(HttpStatusReturningLogoutSuccessHandler()) http.addFilterBefore(JwtLoginFilter(authenticationManager()), UsernamePasswordAuthenticationFilter::class .java) http.addFilterBefore( JwtAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter::class .java ) } @Bean override fun authenticationManager () : AuthenticationManager { return super .authenticationManager() } @Bean fun passwordEncoder () : BCryptPasswordEncoder { return BCryptPasswordEncoder(); } }
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 package com.holelin.securityimport com.alibaba.fastjson.JSONimport com.alibaba.fastjson.JSONObjectimport com.holelin.util.HttpUtilsimport com.holelin.util.JwtTokenUtilsimport org.springframework.security.authentication.AuthenticationManagerimport org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEventimport org.springframework.security.core.Authenticationimport org.springframework.security.core.AuthenticationExceptionimport org.springframework.security.core.context.SecurityContextHolderimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilterimport java.io.BufferedReaderimport java.io.IOExceptionimport java.io.InputStreamimport java.io.InputStreamReaderimport java.nio.charset.Charsetimport javax.servlet.FilterChainimport javax.servlet.ServletExceptionimport javax.servlet.ServletRequestimport javax.servlet.ServletResponseimport javax.servlet.http.HttpServletRequestimport javax.servlet.http.HttpServletResponseclass JwtLoginFilter (authManager: AuthenticationManager?) : UsernamePasswordAuthenticationFilter() { @Throws(IOException::class, ServletException::class) override fun doFilter (req: ServletRequest , res: ServletResponse , chain: FilterChain ) { super .doFilter(req, res, chain) } @Throws(AuthenticationException::class) override fun attemptAuthentication (request: HttpServletRequest , response: HttpServletResponse ) : Authentication { val body = getBody(request) val jsonObject: JSONObject = JSON.parseObject(body) var username: String = jsonObject.getString("username" ) var password: String = jsonObject.getString("password" ) username = username.trim { it <= ' ' } val authRequest = JwtAuthenticationToken(username, password) setDetails(request, authRequest) return authenticationManager.authenticate(authRequest) } @Throws(IOException::class, ServletException::class) override fun successfulAuthentication ( request: HttpServletRequest ?, response: HttpServletResponse , chain: FilterChain ?, authResult: Authentication ? ) { SecurityContextHolder.getContext().authentication = authResult rememberMeServices.loginSuccess(request, response, authResult) if (eventPublisher != null ) { eventPublisher.publishEvent(InteractiveAuthenticationSuccessEvent(authResult, this .javaClass)) } val token = JwtAuthenticationToken(null , null , authResult?.let { JwtTokenUtils.generateToken(it) }) HttpUtils.write(response, token) } private fun getBody (request: HttpServletRequest ) : String { val sb = StringBuilder() var inputStream: InputStream? = null var reader: BufferedReader? = null try { inputStream = request.inputStream reader = BufferedReader(InputStreamReader(inputStream, Charset.forName("UTF-8" ))) var line: String? = "" while (reader.readLine().also { line = it } != null ) { sb.append(line) } } catch (e: IOException) { e.printStackTrace() } finally { if (inputStream != null ) { try { inputStream.close() } catch (e: IOException) { e.printStackTrace() } } if (reader != null ) { try { reader.close() } catch (e: IOException) { e.printStackTrace() } } } return sb.toString() } init { authenticationManager = authManager } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.holelin.securityimport org.springframework.security.authentication.UsernamePasswordAuthenticationTokenimport org.springframework.security.authentication.dao.DaoAuthenticationProviderimport org.springframework.security.core.Authenticationimport org.springframework.security.core.AuthenticationExceptionimport org.springframework.security.core.userdetails.UserDetailsimport org.springframework.security.core.userdetails.UserDetailsServiceimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoderclass JwtAuthenticationProvider (userDetailsService: UserDetailsService?) : DaoAuthenticationProvider() { @Throws(AuthenticationException::class) override fun authenticate (authentication: Authentication ?) : Authentication { return super .authenticate(authentication) } @Throws(AuthenticationException::class) override fun additionalAuthenticationChecks ( userDetails: UserDetails , authentication: UsernamePasswordAuthenticationToken ) { super .additionalAuthenticationChecks(userDetails, authentication) } init { setUserDetailsService(userDetailsService) passwordEncoder = BCryptPasswordEncoder() } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.holelin.securityimport org.springframework.security.core.GrantedAuthorityimport org.springframework.security.core.userdetails.Userclass JwtUserDetails ( username: String?, password: String?, enabled: Boolean , accountNonExpired: Boolean , credentialsNonExpired: Boolean , accountNonLocked: Boolean , authorities: Collection<GrantedAuthority?>? ) : User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities) { constructor (username: String?, password: String?, authorities: Collection<GrantedAuthority?>?) : this ( username, password, true , true , true , true , authorities ) { } companion object { private const val serialVersionUID = 1L } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.holelin.securityimport org.springframework.security.authentication.UsernamePasswordAuthenticationTokenimport org.springframework.security.core.GrantedAuthorityclass JwtAuthenticationToken : UsernamePasswordAuthenticationToken { var token: String? = null constructor (principal: Any?, credentials: Any?) : super (principal, credentials) {} constructor (principal: Any?, credentials: Any?, token: String?) : super (principal, credentials) { this .token = token } constructor ( principal: Any?, credentials: Any?, authorities: Collection<GrantedAuthority?>?, token: String? ) : super (principal, credentials, authorities) { this .token = token } companion object { const val serialVersionUID = 1L } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.holelin.securityimport com.holelin.util.SecurityUtilsimport org.springframework.beans.factory.annotation .Autowiredimport org.springframework.security.authentication.AuthenticationManagerimport org.springframework.security.web.authentication.www.BasicAuthenticationFilterimport java.io.IOExceptionimport javax.servlet.FilterChainimport javax.servlet.ServletExceptionimport javax.servlet.http.HttpServletRequestimport javax.servlet.http.HttpServletResponseclass JwtAuthenticationFilter @Autowired constructor (authenticationManager: AuthenticationManager?) : BasicAuthenticationFilter(authenticationManager) { @Throws(IOException::class, ServletException::class) override fun doFilterInternal (request: HttpServletRequest , response: HttpServletResponse , chain: FilterChain ) { SecurityUtils.checkAuthentication(request) chain.doFilter(request, response) } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.holelin.securityimport org.springframework.security.core.GrantedAuthorityclass GrantedAuthorityImpl (private var authority: String) : GrantedAuthority { fun setAuthority (authority: String ) { this .authority = authority } override fun getAuthority () : String { return authority } companion object { private const val serialVersionUID = 1L } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.holelin.securityimport org.springframework.security.core.GrantedAuthorityimport org.springframework.security.core.userdetails.Userclass JwtUserDetails ( username: String?, password: String?, enabled: Boolean , accountNonExpired: Boolean , credentialsNonExpired: Boolean , accountNonLocked: Boolean , authorities: Collection<GrantedAuthority?>? ) : User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities) { constructor (username: String?, password: String?, authorities: Collection<GrantedAuthority?>?) : this ( username, password, true , true , true , true , authorities ) { } companion object { private const val serialVersionUID = 1L } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.holelin.utilimport com.alibaba.fastjson.JSONObjectimport com.holelin.vo.HttpResultimport org.springframework.web.context.request.RequestContextHolderimport org.springframework.web.context.request.ServletRequestAttributesimport java.io.IOExceptionimport javax.servlet.http.HttpServletRequestimport javax.servlet.http.HttpServletResponseobject HttpUtils { val httpServletRequest: HttpServletRequest get () = (RequestContextHolder.getRequestAttributes() as ServletRequestAttributes).request @Throws(IOException::class) fun write (response: HttpServletResponse , data : Any ?) { response.contentType = "application/json; charset=utf-8" val result: HttpResult = HttpResult.ok(data ) val json: String = JSONObject.toJSONString(result) response.writer.print(json) response.writer.flush() response.writer.close() } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 package com.holelin.utilimport com.holelin.security.GrantedAuthorityImplimport com.holelin.security.JwtAuthenticationTokenimport io.jsonwebtoken.Claimsimport io.jsonwebtoken.Jwtsimport io.jsonwebtoken.SignatureAlgorithmimport org.springframework.security.core.Authenticationimport org.springframework.security.core.GrantedAuthorityimport java.io.Serializableimport java.util.*import javax.servlet.http.HttpServletRequestimport kotlin.collections.ArrayListimport kotlin.collections.HashMapobject JwtTokenUtils : Serializable { private const val serialVersionUID = 1L private const val USERNAME = Claims.SUBJECT private const val CREATED = "created" private const val AUTHORITIES = "authorities" private const val SECRET = "abcdefgh" private const val EXPIRE_TIME = (12 * 60 * 60 * 1000 ).toLong() fun generateToken (authentication: Authentication ) : String { val claims: MutableMap<String, Any> = HashMap(3 ) claims[USERNAME] = SecurityUtils.getUsername(authentication) claims[CREATED] = Date() claims[AUTHORITIES] = authentication.authorities return generateToken(claims) } private fun generateToken (claims: Map <String , Any>) : String { val expirationDate = Date(System.currentTimeMillis() + EXPIRE_TIME) return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, SECRET) .compact() } private fun getUsernameFromToken (token: String ) : String? { val username: String? username = try { val claims = getClaimsFromToken(token) claims!!.subject } catch (e: Exception) { null } return username } fun getAuthenticationFromToken (request: HttpServletRequest ) : Authentication? { var authentication: Authentication? = null val token = getToken(request) if (token != null ) { if (SecurityUtils.authentication == null ) { val claims = getClaimsFromToken(token) ?: return null val username = claims.subject ?: return null if (isTokenExpired(token)) { return null } val authors = claims[AUTHORITIES] val authorities: MutableList<GrantedAuthority> = ArrayList() if (authors != null && authors is List<*>) { for (`object ` in authors) { authorities.add(GrantedAuthorityImpl((`object ` as Map<*, *>)["authority" ] as String)) } } authentication = JwtAuthenticationToken(username, null , authorities, token) } else { if (validateToken(token, SecurityUtils.username)) { authentication = SecurityUtils.authentication } } } return authentication } private fun getClaimsFromToken (token: String ) : Claims? { val claims: Claims? = try { Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).body } catch (e: Exception) { null } return claims } private fun validateToken (token: String , username: String ) : Boolean { val userName = getUsernameFromToken(token) return userName == username && !isTokenExpired(token) } fun refreshToken (token: String ) : String? { var refreshedToken: String? try { val claims = getClaimsFromToken(token) claims!![CREATED] = Date() refreshedToken = generateToken(claims) } catch (e: Exception) { refreshedToken = null } return refreshedToken } private fun isTokenExpired (token: String ) : Boolean { return try { val claims = getClaimsFromToken(token) val expiration: Date = claims!!.expiration expiration.before(Date()) } catch (e: Exception) { false } } private fun getToken (request: HttpServletRequest ) : String? { var token = request.getHeader("Authorization" ) val tokenHead = "Bearer " if (token == null ) { token = request.getHeader("token" ) } else if (token.contains(tokenHead)) { token = token.substring(tokenHead.length) } if ("" == token) { token = null } return token } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 package com.holelin.utilimport com.holelin.security.JwtAuthenticationTokenimport org.springframework.security.authentication.AuthenticationManagerimport org.springframework.security.core.Authenticationimport org.springframework.security.core.context.SecurityContextHolderimport org.springframework.security.core.userdetails.UserDetailsimport org.springframework.security.web.authentication.WebAuthenticationDetailsSourceimport javax.servlet.http.HttpServletRequestobject SecurityUtils { fun login ( request: HttpServletRequest ?, username: String ?, password: String ?, authenticationManager: AuthenticationManager ) : JwtAuthenticationToken { val token = JwtAuthenticationToken(username, password) token.details = WebAuthenticationDetailsSource().buildDetails(request) val authentication: Authentication = authenticationManager.authenticate(token) SecurityContextHolder.getContext().authentication = authentication token.token = JwtTokenUtils.generateToken(authentication) return token } fun checkAuthentication (request: HttpServletRequest ?) { val authentication: Authentication? = JwtTokenUtils.getAuthenticationFromToken(request!!) SecurityContextHolder.getContext().authentication = authentication } val username: String get () { var username = "" val authentication: Authentication? = authentication if (authentication != null ) { val principal: Any = authentication.principal if (principal is UserDetails) { username = principal.username } } return username } fun getUsername (authentication: Authentication ) : String { var username: String = "" val principal: Any = authentication.principal if (principal is UserDetails) { username = principal.username } return username } val authentication: Authentication? get () = if (SecurityContextHolder.getContext() == null ) { null } else SecurityContextHolder.getContext().authentication }
基于SpringBoot 2.7.4
依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-security</artifactId > </dependency > <dependency > <groupId > io.jsonwebtoken</groupId > <artifactId > jjwt-api</artifactId > </dependency > <dependency > <groupId > io.jsonwebtoken</groupId > <artifactId > jjwt-impl</artifactId > <scope > runtime</scope > </dependency > <dependency > <groupId > io.jsonwebtoken</groupId > <artifactId > jjwt-jackson</artifactId > <scope > runtime</scope > </dependency >
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 package cn.holelin.athena.config;import cn.holelin.athena.config.security.CustomizeAccessDeniedHandler;import cn.holelin.athena.config.security.ResourceAuthExceptionEntryPoint;import cn.holelin.athena.config.security.SecuritySecurityCheckService;import cn.holelin.athena.filter.JwtAuthenticationTokenFilter;import cn.holelin.athena.service.NotAuthenticationService;import cn.holelin.athena.service.UserDetailsServiceImpl;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.http.SessionCreationPolicy;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.web.SecurityFilterChain;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfiguration { private final UserDetailsServiceImpl userDetailsService; private final NotAuthenticationService notAuthenticationService; public SecurityConfiguration (UserDetailsServiceImpl userDetailsService, NotAuthenticationService notAuthenticationService) { this .userDetailsService = userDetailsService; this .notAuthenticationService = notAuthenticationService; } @Bean public SecurityFilterChain filterChain (HttpSecurity http) throws Exception { http.cors().and() .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and().authorizeRequests(rep -> rep.antMatchers(notAuthenticationService.getPermitAllUrls().toArray(new String [0 ])) .permitAll().anyRequest().authenticated()) .exceptionHandling() .authenticationEntryPoint(new ResourceAuthExceptionEntryPoint ()) .accessDeniedHandler(new CustomizeAccessDeniedHandler ()) .and() .addFilterBefore(new JwtAuthenticationTokenFilter (), UsernamePasswordAuthenticationFilter.class) .userDetailsService(userDetailsService); return http.build(); } @Bean public PasswordEncoder passwordEncoder () { return new BCryptPasswordEncoder (); } @Bean public AuthenticationManager authenticationManager (AuthenticationConfiguration configuration) throws Exception { return configuration.getAuthenticationManager(); } @Bean("ssc") public SecuritySecurityCheckService permissionService () { return new SecuritySecurityCheckService (); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package cn.holelin.athena.service;import cn.hutool.core.collection.CollUtil;import com.google.common.collect.Sets;import cn.holelin.athena.entity.SysRole;import cn.holelin.athena.entity.SysUser;import cn.holelin.athena.mapper.SysUserMapper;import cn.holelin.athena.mapper.SysUserRoleMapper;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.AuthorityUtils;import org.springframework.security.core.userdetails.User;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.stereotype.Service;import java.util.Collection;import java.util.List;import java.util.Objects;import java.util.Set;import java.util.stream.Collectors;@Service public class UserDetailsServiceImpl implements UserDetailsService { private final SysUserMapper sysUserMapper; private final SysUserRoleMapper sysUserRoleMapper; public UserDetailsServiceImpl (SysUserMapper sysUserMapper, SysUserRoleMapper sysUserRoleMapper) { this .sysUserMapper = sysUserMapper; this .sysUserRoleMapper = sysUserRoleMapper; } @Override public UserDetails loadUserByUsername (String username) throws UsernameNotFoundException { final SysUser sysUser = sysUserMapper.queryByUsername(username); if (Objects.isNull(sysUser)) { throw new UsernameNotFoundException ("账号不存在" ); } final List<SysRole> sysRoles = sysUserRoleMapper.queryByUsername(username); Set<String> dbAuthsSet = Sets.newHashSet(); if (CollUtil.isNotEmpty(sysRoles)) { dbAuthsSet = sysRoles.stream().map(SysRole::getFlag).collect(Collectors.toSet()); } Collection<GrantedAuthority> auths = AuthorityUtils .createAuthorityList(dbAuthsSet.toArray(new String [0 ])); return new User (sysUser.getUsername(), new BCryptPasswordEncoder ().encode(sysUser.getPassword()), auths); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package cn.holelin.athena.service;import cn.holelin.athena.annotation.NotAuthentication;import lombok.Getter;import lombok.Setter;import org.springframework.beans.BeansException;import org.springframework.beans.factory.InitializingBean;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.stereotype.Service;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.mvc.method.RequestMappingInfo;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.Objects;import java.util.Optional;@Service public class NotAuthenticationService implements InitializingBean , ApplicationContextAware { private static final String PATTERN = "\\{(.*?)}" ; public static final String ASTERISK = "*" ; private ApplicationContext applicationContext; @Getter @Setter private List<String> permitAllUrls = new ArrayList <>(); @Override public void afterPropertiesSet () throws Exception { RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods(); map.keySet().forEach(x -> { HandlerMethod handlerMethod = map.get(x); NotAuthentication method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), NotAuthentication.class); Optional.ofNullable(method).ifPresent(inner -> Objects.requireNonNull(x.getPathPatternsCondition()) .getPatternValues().forEach(url -> permitAllUrls.add(url.replaceAll(PATTERN, ASTERISK)))); NotAuthentication controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), NotAuthentication.class); Optional.ofNullable(controller).ifPresent(inner -> Objects.requireNonNull(x.getPathPatternsCondition()) .getPatternValues().forEach(url -> permitAllUrls.add(url.replaceAll(PATTERN, ASTERISK)))); }); } @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { this .applicationContext = applicationContext; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package cn.holelin.athena.config.security;import com.alibaba.fastjson2.JSON;import cn.holelin.base.ResponseMessage;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.AuthenticationEntryPoint;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class ResourceAuthExceptionEntryPoint implements AuthenticationEntryPoint { @Override public void commence (HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { response.setCharacterEncoding("UTF-8" ); response.setContentType("application/json; charset=utf-8" ); response.getWriter().write(JSON.toJSONString(ResponseMessage.error("认证失败" ))); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package cn.holelin.athena.config.security;import com.alibaba.fastjson2.JSON;import cn.holelin.base.ResponseMessage;import org.springframework.security.access.AccessDeniedException;import org.springframework.security.web.access.AccessDeniedHandler;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class CustomizeAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle (HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { response.setContentType("text/json;charset=utf-8" ); response.getWriter().write(JSON.toJSONString(ResponseMessage.error("权限验证失败" ))); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 package cn.holelin.athena.filter;import cn.holelin.athena.constants.SecurityConstants;import cn.holelin.athena.utils.JwtUtil;import io.jsonwebtoken.Claims;import io.jsonwebtoken.ExpiredJwtException;import io.jsonwebtoken.MalformedJwtException;import io.jsonwebtoken.UnsupportedJwtException;import io.jsonwebtoken.security.SignatureException;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.ArrayList;import java.util.List;import java.util.Optional;import java.util.stream.Collectors;import static cn.holelin.athena.constants.SecurityConstants.JWT_TOKEN_HEADER_KEY;public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Override protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { if (checkJWTToken(request)) { Optional<Claims> claimsOptional = validateToken(request) .filter(claims -> claims.get(SecurityConstants.JWT_TOKEN_ROLE_CLAIM) != null ); if (claimsOptional.isPresent()) { List<String> authoritiesList = castList(claimsOptional.get().get(SecurityConstants.JWT_TOKEN_ROLE_CLAIM), String.class); List<SimpleGrantedAuthority> authorities = authoritiesList .stream().map(String::valueOf) .map(SimpleGrantedAuthority::new ).collect(Collectors.toList()); UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken (claimsOptional.get().getSubject(), null , authorities); SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); } else { SecurityContextHolder.clearContext(); } } chain.doFilter(request, response); } public static <T> List<T> castList (Object obj, Class<T> clazz) { List<T> result = new ArrayList <T>(); if (obj instanceof List<?>) { for (Object o : (List<?>) obj) { result.add(clazz.cast(o)); } return result; } return null ; } private Optional<Claims> validateToken (HttpServletRequest req) { String jwtToken = req.getHeader(JWT_TOKEN_HEADER_KEY); try { return JwtUtil.parseAccessTokenClaims(jwtToken); } catch (ExpiredJwtException | SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) { return Optional.empty(); } } private boolean checkJWTToken (HttpServletRequest request) { String authenticationHeader = request.getHeader(JWT_TOKEN_HEADER_KEY); return authenticationHeader != null ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 package cn.holelin.athena.utils;import io.jsonwebtoken.*;import io.jsonwebtoken.security.SignatureException;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import java.nio.charset.StandardCharsets;import java.security.Key;import java.util.Date;import java.util.Optional;import java.util.UUID;import java.util.stream.Collectors;import static cn.holelin.athena.constants.SecurityConstants.JWT_ALGORITHM;import static cn.holelin.athena.constants.SecurityConstants.JWT_TOKEN_ROLE_CLAIM;public class JwtUtil { private static final Long ACCESS_TOKEN_EXPIRE_TIME = 60 * 60 * 1000L ; private static final String ACCESS_TOKEN_KEY = "HOLELINQAZXSWEDCVFRTGBNHYUJM...ASDJLKASDNAS,DMNIUQWHEKASNDLASDOIQJWEQWEMASMD" ; private static final Long REFRESH_TOKEN_EXPIRE_TIME = 24 * 60 * 60 * 1000L ; private static final String REFRESH_TOKEN_KEY = "...MJUYHNBGTRFVCDEWXZAQHOLELINAJDASJDQWEULCKNHASDOIADNABDAISHDASLDKJASDUHCASDJH" ; private JwtUtil () { } public static String getUUID () { return UUID.randomUUID().toString().replace("-" , "" ); } public static String createJWTToken (UserDetails userDetails, long timeToExpire) { return createJWTToken(userDetails, timeToExpire, new SecretKeySpec (ACCESS_TOKEN_KEY.getBytes(StandardCharsets.UTF_8), JWT_ALGORITHM)); } public static String createAccessToken (UserDetails userDetails) { return createJWTToken(userDetails, ACCESS_TOKEN_EXPIRE_TIME); } public static String createRefreshToken (UserDetails userDetails) { return createJWTToken(userDetails, REFRESH_TOKEN_EXPIRE_TIME, new SecretKeySpec (REFRESH_TOKEN_KEY.getBytes(StandardCharsets.UTF_8), JWT_ALGORITHM)); } private static String createJWTToken (UserDetails userDetails, long timeToExpire, Key signKey) { return Jwts.builder() .id(getUUID()) .subject(userDetails.getUsername()) .claim(JWT_TOKEN_ROLE_CLAIM, userDetails.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.toList())) .issuedAt(new Date (System.currentTimeMillis())) .expiration(new Date (System.currentTimeMillis() + timeToExpire)) .signWith(signKey).compact(); } public static boolean validateAccessToken (String jwtToken) { return validateToken(jwtToken, new SecretKeySpec (ACCESS_TOKEN_KEY.getBytes(StandardCharsets.UTF_8), JWT_ALGORITHM)); } public static boolean validateRefreshToken (String jwtToken) { return validateToken(jwtToken, new SecretKeySpec (REFRESH_TOKEN_KEY.getBytes(StandardCharsets.UTF_8), JWT_ALGORITHM)); } public static boolean validateToken (String jwtToken, SecretKey signKey) { return parseClaims(jwtToken, signKey).isPresent(); } public static Optional<Claims> parseAccessTokenClaims (String jwtToken) { return Optional.ofNullable(Jwts.parser().verifyWith(new SecretKeySpec (ACCESS_TOKEN_KEY.getBytes(StandardCharsets.UTF_8), JWT_ALGORITHM)) .build().parseSignedClaims(jwtToken).getPayload()); } public static Optional<Claims> parseRefreshTokenClaims (String jwtToken) { return Optional.ofNullable(Jwts.parser().verifyWith(new SecretKeySpec (REFRESH_TOKEN_KEY.getBytes(StandardCharsets.UTF_8), JWT_ALGORITHM)) .build().parseSignedClaims(jwtToken).getPayload()); } public static Optional<Claims> parseClaims (String jwtToken, SecretKey signKey) { return Optional.ofNullable(Jwts.parser().verifyWith(signKey).build().parseSignedClaims(jwtToken).getPayload()); } public static boolean validateWithoutExpiration (String jwtToken) { try { Jwts.parser().verifyWith(new SecretKeySpec (ACCESS_TOKEN_KEY.getBytes(StandardCharsets.UTF_8), JWT_ALGORITHM)) .build().parseSignedClaims(jwtToken); return true ; } catch (ExpiredJwtException | SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) { if (e instanceof ExpiredJwtException) { return true ; } } return false ; } }
1 2 3 4 5 6 7 8 9 10 11 12 package cn.holelin.athena.constants;public class SecurityConstants { private SecurityConstants () { } public static final String JWT_ALGORITHM = "HmacSHA512" ; public static final String JWT_TOKEN_ROLE_CLAIM = "authorities" ; public static final String JWT_TOKEN_HEADER_KEY = "token" ; }
注意点
Be careful when you declare your filter as a Spring bean, either by annotating it with @Component or by declaring it as a bean in your configuration, because Spring Boot will automatically register it with the embedded container. That may cause the filter to be invoked twice, once by the container and once by Spring Security and in a different order.
在将过滤器声明为Spring bean时要小心,要么用@Component注解,要么在配置中将其声明为bean,因为Spring Boot会自动将其注册到嵌入的容器中.这可能会导致过滤器被调用两次,一次由容器调用,一次由Spring Security调用,而且调用顺序不同.
If you still want to declare your filter as a Spring bean to take advantage of dependency injection for example, and avoid the duplicate invocation, you can tell Spring Boot to not register it with the container by declaring a FilterRegistrationBean
bean and setting its enabled
property to false
:
1 2 3 4 5 6 @Bean public FilterRegistrationBean<TenantFilter> tenantFilterRegistration (TenantFilter filter) { FilterRegistrationBean<TenantFilter> registration = new FilterRegistrationBean <>(filter); registration.setEnabled(false ); return registration; }
使用场景
用户登录和认证: Spring Security可以处理用户的身份验证,包括用户名密码验证、基于数据库或LDAP的用户存储等。它提供了多种身份验证机制,如表单登录、基本认证、OAuth等。
授权和权限管理: Spring Security允许定义安全规则和访问控制,以确保用户只能访问其有权访问的资源。可以使用注解、表达式或配置文件来声明和管理权限。
防止跨站点请求伪造(CSRF): Spring Security可以生成和验证CSRF令牌,以防止Web应用程序受到CSRF攻击。它可以在表单中自动添加CSRF令牌,并验证提交请求中的令牌值。
方法级安全性: Spring Security允许在方法级别对方法进行安全性配置。可以使用注解或表达式来定义哪些用户有权调用特定的方法。
记住我功能: Spring Security提供了记住我功能,允许用户在下次访问时保持登录状态,而不需要重新输入用户名和密码。
单点登录(SSO): Spring Security可以与其他身份验证和授权提供程序集成,实现单点登录功能。用户只需登录一次,即可在不同的应用程序之间共享身份验证信息。
安全事件和审计日志: Spring Security可以记录安全事件和用户操作,以便进行审计和故障排查。可以配置事件监听器和审计日志记录器来记录关键的安全事件。
过滤器
默认过滤器并不是直接放在Web项目的原生过滤器链中,而是通过FilterChainProxy
来统一管理.
FilterChainProxy
作为一个顶层管理者,将通过管理Security Filter
.FilterChainProxy
本身将通过Spring框架提供的DelegatingFilterProxy
整合到原生过滤链中.
常见的过滤器
过滤器
过滤器作用
ForceEagerSessionCreationFilter
在某些情况下强制创建 HttpSession
ChannelProcessingFilter
处理 HTTPS
和 HTTP
之间的重定向
WebAsyncManagerIntegrationFilter
将WebAsyncManager
与Spring Security
上下文集成
SecurityContextHolderFilter
SecurityContextPersistenceFilter
在处理请求之前,将安全信息加载到SecurityContextHolder
中以方便后续使用.请求结束后再擦除SecurityContextHolder
中的信息
HeaderWriterFilter
头信息加入到响应中
CorsFilter
处理跨域问题
CsrfFilter
防止 CSRF 攻击
LogoutFilter
注销当前用户
OAuth2AuthorizationRequestRedirectFilter
处理OAuth2认证重定向
Saml2WebSsoAuthenticationRequestFilter
处理SAML认证
X509AuthenticationFilter
处理X509认证
AbstractAuthenticationProcessingFilter
基于浏览器的http认证请求的抽象处理器。
CasAuthenticationFilter
处理CAS单点登录
OAuth2LoginAuthenticationFilter
处理OAuth2认证
Saml2WebSsoAuthenticationFilter
处理SAML认证
UsernamePasswordAuthenticationFilter
处理表单登录
DefaultLoginPageGeneratingFilter
生成默认的登录页面
DefaultLogoutPageGeneratingFilter
生成默认的注销页面
ConcurrentSessionFilter
处理Session有效期
DigestAuthenticationFilter
处理HTTP摘要认证
BearerTokenAuthenticationFilter
处理OAuth2认证时的Access Token
BasicAuthenticationFilter
处理HttpBasic登录
RequestCacheAwareFilter
请求缓存支持
SecurityContextHolderAwareRequestFilter
将请求包装成对 SecurityContext
的支持
JaasApiIntegrationFilter
JAAS 集成
RememberMeAuthenticationFilter
使用“记住我”身份验证
AnonymousAuthenticationFilter
匿名身份验证支持
OAuth2AuthorizationCodeGranttFilter
处理OAuth2认证的授权码
SessionManagermentFilter
处理Session并发问题
ExceptionTranslationFilter
处理异常并执行相应操作
FilterSecurityInterceptor
主要做认证和授权拦截,新版被AuthorizationFilter
代替
AuthorizationFilter
主要做认证和授权拦截
SwitchUserFilter
处理账户切换
源码分析
核心组件
org.springframework.security.web.FilterChainProxy
FilterChainProxy
可以认为是整个Spring Security
处理请求的一个起点,如果你遇到Security相关问题,又不清楚是具体哪个Filter
导致的,就可以从这里开始Debug
.
org.springframework.security.web.SecurityFilterChain
查看过滤器顺序org.springframework.security.config.annotation.web.builders.FilterOrderRegistration
异常处理Filter
org.springframework.security.web.access.ExceptionTranslationFilter
图片来源于https://docs.spring.io/spring-security/reference/servlet/architecture.html
认证Authentication
核心组件
SecurityContextHolder
- SecurityContextHolder
是 Spring Security 存储经过身份验证的详细信息的位置.
SecurityContext
- 从 SecurityContextHolder
获取,包含当前经过身份验证的用户的 Authentication
.
Authentication
- 可以是 AuthenticationManager
的输入,以提供用户提供的用于身份验证的凭据或来自 SecurityContext
的当前用户.
GrantedAuthority
- 授予 Authentication
上主体的权限(即角色、范围等)
AuthenticationManager
它的入参和出参都是Authentication对象。
通常情况下,入参提供了必要的认证信息,例如用户名和密码。而在认证成功后,该方法会返回认证结果,并附加认证状态,用户拥有的权限列表等信息。
如果认证失败,它会抛出AuthenticationException异常类的子类,其中包括DisabledException,LockedException和BadCredentialsException等账号相关的异常
AuthenticationProvider
- 身份验证提供程序是实际执行身份验证的组件。它从用户存储源(如数据库、LDAP等)中获取用户信息,并进行密码比对或其他验证方式,确定用户的身份是否有效。Spring Security提供了多种身份验证提供程序的实现,如基于数据库的验证、基于LDAP的验证、OpenID验证等,由 ProviderManager
使用来执行特定类型的身份验证.
ProviderManager
- AuthenticationManager
最常见的实现.
使用 AuthenticationEntryPoint
请求凭证 - 用于从客户端请求凭证(即重定向到登录页面、发送 WWW-Authenticate
响应等)
AbstractAuthenticationProcessingFilter
- 用于身份验证的基础 Filter
.
在Spring Security
中,很多初学者都容易混淆Role
和Authority
的区别,实际上在技术实现层面上,这两者没有本质区别,底层都仅仅是一个表示权限的字符串标识符.更多的区别在于权限管理的概念上,一般情况下,Authority
表示细粒度的操作权限,比如ADD_USER
,DELETE_USER
等,通常是动词;而Role
则会与实际业务角色想对应,比如管理员ADMIN
,普通员工STAFF
等,通常是名称.此外,一般一个Role
会对应多个Authority
,同时角色之间可以存在继承关系,比如ADMIN
可以继承STAFF
的所有权限
1 2 3 4 5 6 AbstractAuthenticationProcessingFilter#doFilter -->子类(UsernamePasswordAuthenticationFilter)的attemptAuthentication-->AuthenticationManager -->子类(ProviderManager) -->AuthenticationProvider#authenticate -->AbstractUserDetailsAuthenticationProvider#retrieveUser -->DaoAuthenticationProvider#retrieveUser
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter#doFilter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public void doFilter (ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; if (!requiresAuthentication(request, response)) { chain.doFilter(request, response); return ; } if (logger.isDebugEnabled()) { logger.debug("Request is to process authentication" ); } Authentication authResult; try { authResult = attemptAuthentication(request, response); if (authResult == null ) { return ; } sessionStrategy.onAuthentication(authResult, request, response); } catch (InternalAuthenticationServiceException failed) { logger.error( "An internal error occurred while trying to authenticate the user." , failed); unsuccessfulAuthentication(request, response, failed); return ; } catch (AuthenticationException failed) { unsuccessfulAuthentication(request, response, failed); return ; } if (continueChainBeforeSuccessfulAuthentication) { chain.doFilter(request, response); } successfulAuthentication(request, response, chain, authResult); }
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public Authentication attemptAuthentication (HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (postOnly && !request.getMethod().equals("POST" )) { throw new AuthenticationServiceException ( "Authentication method not supported: " + request.getMethod()); } String username = obtainUsername(request); String password = obtainPassword(request); if (username == null ) { username = "" ; } if (password == null ) { password = "" ; } username = username.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken ( username, password); setDetails(request, authRequest); return this .getAuthenticationManager().authenticate(authRequest); }
org.springframework.security.authentication.ProviderManager#authenticate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 public Authentication authenticate (Authentication authentication) throws AuthenticationException { Class<? extends Authentication > toTest = authentication.getClass(); AuthenticationException lastException = null ; AuthenticationException parentException = null ; Authentication result = null ; Authentication parentResult = null ; boolean debug = logger.isDebugEnabled(); for (AuthenticationProvider provider : getProviders()) { if (!provider.supports(toTest)) { continue ; } if (debug) { logger.debug("Authentication attempt using " + provider.getClass().getName()); } try { result = provider.authenticate(authentication); if (result != null ) { copyDetails(authentication, result); break ; } } catch (AccountStatusException | InternalAuthenticationServiceException e) { prepareException(e, authentication); throw e; } catch (AuthenticationException e) { lastException = e; } } if (result == null && parent != null ) { try { result = parentResult = parent.authenticate(authentication); } catch (ProviderNotFoundException e) { } catch (AuthenticationException e) { lastException = parentException = e; } } if (result != null ) { if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) { ((CredentialsContainer) result).eraseCredentials(); } if (parentResult == null ) { eventPublisher.publishAuthenticationSuccess(result); } return result; } if (lastException == null ) { lastException = new ProviderNotFoundException (messages.getMessage( "ProviderManager.providerNotFound" , new Object [] { toTest.getName() }, "No AuthenticationProvider found for {0}" )); } if (parentException == null ) { prepareException(lastException, authentication); } throw lastException; }
org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider#authenticate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 public Authentication authenticate (Authentication authentication) throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> messages.getMessage( "AbstractUserDetailsAuthenticationProvider.onlySupports" , "Only UsernamePasswordAuthenticationToken is supported" )); String username = (authentication.getPrincipal() == null ) ? "NONE_PROVIDED" : authentication.getName(); boolean cacheWasUsed = true ; UserDetails user = this .userCache.getUserFromCache(username); if (user == null ) { cacheWasUsed = false ; try { user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); } catch (UsernameNotFoundException notFound) { logger.debug("User '" + username + "' not found" ); if (hideUserNotFoundExceptions) { throw new BadCredentialsException (messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials" , "Bad credentials" )); } else { throw notFound; } } Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract" ); } try { preAuthenticationChecks.check(user); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } catch (AuthenticationException exception) { if (cacheWasUsed) { cacheWasUsed = false ; user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); preAuthenticationChecks.check(user); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } else { throw exception; } } postAuthenticationChecks.check(user); if (!cacheWasUsed) { this .userCache.putUserInCache(user); } Object principalToReturn = user; if (forcePrincipalAsString) { principalToReturn = user.getUsername(); } return createSuccessAuthentication(principalToReturn, authentication, user); }
org.springframework.security.authentication.dao.DaoAuthenticationProvider#retrieveUser
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 protected final UserDetails retrieveUser (String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { prepareTimingAttackProtection(); try { UserDetails loadedUser = this .getUserDetailsService().loadUserByUsername(username); if (loadedUser == null ) { throw new InternalAuthenticationServiceException ( "UserDetailsService returned null, which is an interface contract violation" ); } return loadedUser; } catch (UsernameNotFoundException ex) { mitigateAgainstTimingAttack(authentication); throw ex; } catch (InternalAuthenticationServiceException ex) { throw ex; } catch (Exception ex) { throw new InternalAuthenticationServiceException (ex.getMessage(), ex); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public interface UserDetailsService { UserDetails loadUserByUsername (String username) throws UsernameNotFoundException; }
默认生成的生成的用户名和密码如何生成
授权Authorization
The first thing this provides is an enhanced set of authorization fields and methods to your SpEL expressions. What follows is a quick overview of the most common methods:
permitAll
- The request requires no authorization to be invoked; note that in this case, the Authentication
is never retrieved from the session
denyAll
- The request is not allowed under any circumstances; note that in this case, the Authentication
is never retrieved from the session
hasAuthority
- The request requires that the Authentication
have a GrantedAuthority
that matches the given value
hasRole
- A shortcut for hasAuthority
that prefixes ROLE_
or whatever is configured as the default prefix
hasAnyAuthority
- The request requires that the Authentication
have a GrantedAuthority
that matches any of the given values
hasAnyRole
- A shortcut for hasAnyAuthority
that prefixes ROLE_
or whatever is configured as the default prefix
hasPermission
- A hook into your PermissionEvaluator
instance for doing object-level authorization
异常处理
Spring Security
创建使用session
的方法