A preface
Hi, I’m a Knowledge seeker and this is the fourth article in springSecurity. For those of you who don’t have the basic knowledge, please come back to this article after studying. Source code address attached at the end of the article;
The pom
The poM file introduces the dependency that the security initiator supports the security function; Lombok simplifies development; Fastjson for Json processing;
JJWT for JWT token support; Lang3 string processing;
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<scope>provided</scope>
</dependency>
<! -- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
</dependencies>
Copy the code
Iii. Certification Process
SecurityContextHolder
To provideSecurityContext
Access permission to.SecurityContext
, saveAuthentication
And possibly request specific security information.Authentication
Represents validation in a Spring Security-specific manner.GrantedAuthority
To reflect the application-wide permissions granted to the principal.UserDetails
Provides the information needed to build an Authentication object from the application’s DAO or other secure data source.UserDetailsService
, based on theString
Is created when the user name (or certificate ID, etc.) is passedUserDetails
.
Get the user information from the data source and assemble it into the UserDetails, then pass the UserDetailsService the UserDetails. The SecurityContextHolder stores the entire user context and stores Authentication through the SecurityContext, thus ensuring that springSecurity holds the user information;
Four entities
SysUser implements UserDetails to store user information, mainly the user name, password, and permission.
/ * * *@Author lsc
* <p> </p>
*/
@Data
public class SysUser implements UserDetails {
/ / user name
private String username;
/ / password
private String password;
// Permission information
private Set<? extends GrantedAuthority> authorities;
@Override
public boolean isAccountNonExpired(a) {
return true;
}
@Override
public boolean isAccountNonLocked(a) {
return true;
}
@Override
public boolean isCredentialsNonExpired(a) {
return true;
}
@Override
public boolean isEnabled(a) {
return true; }}Copy the code
Five Token utility classes
The token tool class is mainly used to produce, parse and verify tokens. What needs to be noted here is that the permission is merged into the step of generating toEKN, so that the permission can be obtained through token, and the permission information can be obtained through token during the permission verification. Disadvantages on the authorization after the token is not updated will cause permissions not synchronized;
/ * * *@Author lsc
* <p> </p>
*/
public class JwtUtil {
private static final String CLAIMS_ROLE = "zszxzRoles";
/** * 5 days */
private static final long EXPIRATION_TIME = 1000 * 60 * 60 * 5;
/** * JWT password */
private static final String SECRET = "secret";
/** * issue JWT */
public static String getToken(String username, String roles) {
Map<String, Object> claims = new HashMap<>(8);
/ / the main body
claims.put( CLAIMS_ROLE, roles);
return Jwts.builder()
.setClaims(claims)
.claim("username",username)
.setExpiration( new Date( Instant.now().toEpochMilli() + EXPIRATION_TIME ) )// Expiration time
.signWith( SignatureAlgorithm.HS512, SECRET )/ / encryption
.compact();
}
/** * verify JWT */
public static Boolean validateToken(String token) {
return(! isTokenExpired( token )); }/** * Whether the obtained token expires */
public static Boolean isTokenExpired(String token) {
Date expiration = getExpireTime( token );
return expiration.before( new Date() );
}
/** * Get username */ based on token
public static String getUsernameByToken(String token) {
String username = (String) parseToken( token ).get("username");
return username;
}
public static Set<GrantedAuthority> getRolseByToken(String token) {
String rolse = (String) parseToken( token ).get(CLAIMS_ROLE);
String[] strArray = StringUtils.strip(rolse, "[]").split(",");
Set<GrantedAuthority> authoritiesSet = new HashSet();
if (strArray.length>0){
Arrays.stream(strArray).forEach(rols-> {
GrantedAuthority authority = new SimpleGrantedAuthority(rols);
authoritiesSet.add(authority);
});
}
return authoritiesSet;
}
/** * The expiration time of the token */
public static Date getExpireTime(String token) {
Date expiration = parseToken( token ).getExpiration();
return expiration;
}
/** * parse JWT */
private static Claims parseToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey( SECRET )
.parseClaimsJws( token )
.getBody();
returnclaims; }}Copy the code
Six UserDetailsService
UserDetailsService The user queries the database data, encapsulates the user data to UserDetails, and goes this way during user identity authentication. The PasswordEncoder provided by the official is used for encryption. The configuration mode needs to be configured in WebSecurityConfig.
/ * * *@Author lsc
* <p> </p>
*/
@Component
@Slf4j
public class SysUserDetailsService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
// During login authentication, obtain all permission information of the user through username. In the formal environment, this is the authorization for querying user data
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("------ user {} authentication -----",username);
// Create a user
SysUser user = new SysUser();
/ / account
user.setUsername(username);
/ / password
user.setPassword(passwordEncoder.encode("123456"));
// Set permissions
Set authoritiesSet = new HashSet();
// Notice Role permissions must be prefixed with ROLE_. Otherwise, 403 is reported
GrantedAuthority userPower = new SimpleGrantedAuthority("ROLE_USER");
GrantedAuthority adminPower = new SimpleGrantedAuthority("ROLE_ADMIN");
authoritiesSet.add(userPower);
authoritiesSet.add(adminPower);
user.setAuthorities(authoritiesSet);
returnuser; }}Copy the code
Seven JWTLoginFilter
JWTLoginFilter inheritance AbstractAuthenticationProcessingFilter filter; Inheritance UsernamePasswordAuthenticationFilter is feasible in theory, It is, after all UsernamePasswordAuthenticationFilter AbstractAuthenticationProcessingFilter implementation class;
JWTLoginFilter is used for user login authentication, which implements the following three methods:
- AttemptAuthentication is used to attemptAuthentication, and if authentication is successful, the successfulAuthentication method is used. If authentication failure can walk unsuccessfulAuthentication method;
- After successfulAuthentication, you need to generate a token, which is returned to the front end as JSON.
- UnsuccessfulAuthentication authentication failure, we determine from the abnormal information then return an error message to the front;
/ * * *@AuthorLSC * < P > Login authentication filter </p> */
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
public JWTLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) {
super(new AntPathRequestMatcher(defaultFilterProcessesUrl));
setAuthenticationManager(authenticationManager);
}
/ * * *@AuthorLSC * < P > Login authentication </p> *@Param [request, response]
* @Return* /
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
SysUser user = new ObjectMapper().readValue(request.getInputStream(), SysUser.class);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
user.getUsername(),
user.getPassword());
return getAuthenticationManager().authenticate(authenticationToken);
}
/ * * *@AuthorLSC * <p> Logon successfully returns token</p> *@Param [request, res, chain, auth]
* @Return* /
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,FilterChain chain, Authentication auth){
SysUser principal = (SysUser)auth.getPrincipal();
String token = JwtUtil.getToken(principal.getUsername(),principal.getAuthorities().toString());
try {
// If the login succeeds, a message is displayed in json format
response.setContentType("application/json; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter out = response.getWriter();
ResultPage result = ResultPage.sucess(CodeMsg.SUCESS,token);
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
} catch(Exception e1) { e1.printStackTrace(); }}@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
String result="";
// The account has expired
if (failed instanceof AccountExpiredException) {
result="Account expired";
}
// The password is incorrect
else if (failed instanceof BadCredentialsException) {
result="Password error";
}
// The password has expired
else if (failed instanceof CredentialsExpiredException) {
result="Password expired";
}
// The account is unavailable
else if (failed instanceof DisabledException) {
result="Account is not available";
}
// The account is locked
else if (failed instanceof LockedException) {
result="Account locked";
}
// The user does not exist
else if (failed instanceof InternalAuthenticationServiceException) {
result="User does not exist";
}
// Other errors
else{
result="Unknown exception";
}
// Process encoding mode to prevent Chinese garbled characters
response.setContentType("text/json; charset=utf-8");
// Stuff the feedback back to the foreground in HttpServletResponseresponse.getWriter().write(JSON.toJSONString(result)); }}Copy the code
Eight WebSecurityConfig
WebSecurityConfig is the configuration information for springSecurity; You can configure data access permission restriction, authorization exception handling, and account encryption mode.
/ * * *@Author lsc
* <p> </p>
*/
@EnableWebSecurity/ / open springSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
DenyHandler denyHandler;
@Autowired
OutSuccessHandler outSuccessHandler;
@Autowired
SysUserDetailsService userDetailsService;
@Autowired
ExpAuthenticationEntryPoint expAuthenticationEntryPoint;
/ * * * @ Author LSC * authorized < / p > < p > * @ Param [HTTP] * /
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()/ / authorization
.antMatchers("/api/download/**").anonymous()// Anonymous user permissions
.antMatchers("/api/**").hasRole("USER")// Common user permission
.antMatchers("/api/admin/**").hasRole("ADMIN")// Admin permission
.antMatchers("/login").permitAll()
// Others require authorization after access
.anyRequest().authenticated()
.and()/ / exception
.exceptionHandling()
.accessDeniedHandler(denyHandler)// Authorize exception handling
.authenticationEntryPoint(expAuthenticationEntryPoint)// Authentication exception handling
.and()
.logout()
.logoutSuccessHandler(outSuccessHandler)
.and()
.addFilterBefore(new JWTLoginFilter("/login",authenticationManager()),UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JwtAuthenticationFilter(authenticationManager()),UsernamePasswordAuthenticationFilter.class)
.sessionManagement()
// Set the Session creation policy to: Spring Security does not create httpSessions
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable();// Close CSRF otherwise post
}
/* * * @author LSC * Authentication set encryption mode
* @param [auth] */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder(a){
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManager(a) throws Exception {
return super.authenticationManager(); }}Copy the code
Nine Handler
Configuration used in the three classes, respectively is denyHandler, outSuccessHandler, expAuthenticationEntryPoint;
DenyHandler this class is used to verify permissions if there are insufficient permissions
/ * * *@AuthorLSC * <p> Insufficient permission handling </p> */
@Component
public class DenyHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
// Set the response header
httpServletResponse.setContentType("application/json; charset=utf-8");
/ / the return valueResultPage result = ResultPage.error(CodeMsg.PERM_ERROR); httpServletResponse.getWriter().write(JSON.toJSONString(result)); }}Copy the code
OutSuccessHandler (localhost:8080/logout);
/ * * *@Author lsc
* <p> </p>
*/
@Component
public class OutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
// Set the response header
httpServletResponse.setContentType("application/json; charset=utf-8");
/ / the return value
ResultPage result = ResultPage.sucess(CodeMsg.SUCESS,"Logout successful."); httpServletResponse.getWriter().write(JSON.toJSONString(result)); }}Copy the code
ExpAuthenticationEntryPoint is responsible for the identity authentication by exception handling, after every major authentication system has its own AuthenticationEntryPoint implementation;
/ * * *@Author lsc
* <p> </p>
*/
@Component
public class ExpAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
// Set the response header
httpServletResponse.setContentType("application/json; charset=utf-8");
/ / the return valueResultPage result = ResultPage.error(CodeMsg.ACCOUNT_ERROR); httpServletResponse.getWriter().write(JSON.toJSONString(result)); }}Copy the code
Ten Controller
SysUserController is used to provide permission tests
/ * * *@Author lsc
* <p> </p>
*/
@RestController
public class SysUserController {
@GetMapping("api/admin")
@PreAuthorize("hasAuthority('ADMIN')")
public String authAdmin(a) {
return "ADMIN permission required";
}
@GetMapping("api/test")
@PreAuthorize("hasAuthority('USER')")
public String authUser(a) {
return "USER permission required"; }}Copy the code
The overall project structure is as follows
11 test
The user logs in and returns the token
Request interface test, return data
User exit returns a message.
The last
Reference documentation
Blog.csdn.net/Piconjo/art…
www.jianshu.com/p/8bd4a6e27…
www.jianshu.com/p/bd882078f…
Docs. Spring. IO/spring – secu…
Source code address: welcome to pay attention to the public number: knowledge seeker, background reply SpringSecurity this set of tutorials:
- SpringSecurity introduction (1)
- SpringSecurity configuration (2)
- Json interaction between springSecurity login and exit (3)
- SpringSecurity back-end separation integration JWT (4)
- To be continued or updated