SpringSecurity+JWT implements the use of front and back end separation
Create a configuration class SecurityConfig WebSecurityConfigurerAdapter inheritance
package top.ryzeyang.demo.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
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.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import top.ryzeyang.demo.common.filter.JwtAuthenticationTokenFilter;
import top.ryzeyang.demo.utils.JwtTokenUtil;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
final AuthenticationFailureHandler authenticationFailureHandler;
final AuthenticationSuccessHandler authenticationSuccessHandler;
final AuthenticationEntryPoint authenticationEntryPoint;
final AccessDeniedHandler accessDeniedHandler;
final LogoutSuccessHandler logoutSuccessHandler;
public SecurityConfig(AuthenticationFailureHandler authenticationFailureHandler, AuthenticationSuccessHandler authenticationSuccessHandler, AuthenticationEntryPoint authenticationEntryPoint, AccessDeniedHandler accessDeniedHandler, LogoutSuccessHandler logoutSuccessHandler) {
this.authenticationFailureHandler = authenticationFailureHandler;
this.authenticationSuccessHandler = authenticationSuccessHandler;
this.authenticationEntryPoint = authenticationEntryPoint;
this.accessDeniedHandler = accessDeniedHandler;
this.logoutSuccessHandler = logoutSuccessHandler;
}
@Bean
public PasswordEncoder passwordEncoder(a) {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean("users")
public UserDetailsService users(a) {
UserDetails user = User.builder()
.username("user")
.password("{bcrypt}$2a$10$1.NSMxlOyMgJHxOi8CWwxuU83G0/HItXxRoBO4QWZMTDp0tzPbCf.")
.roles("USER")
// Roles and authorities cannot coexist
// .authorities(new String[]{"system:user:query", "system:user:edit", "system:user:delete"})
.build();
UserDetails admin = User.builder()
.username("admin")
.password("{bcrypt}$2a$10$1.NSMxlOyMgJHxOi8CWwxuU83G0/HItXxRoBO4QWZMTDp0tzPbCf.")
// .roles("USER", "ADMIN")
.roles("ADMIN")
// .authorities("system:user:create")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
/** * Role inheritance: * Give the Admin role the permissions of the User role *@return* /
@Bean
public RoleHierarchy roleHierarchy(a) {
RoleHierarchyImpl result = new RoleHierarchyImpl();
result.setHierarchy("ROLE_ADMIN > ROLE_USER");
return result;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(users());
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/js/**"."/css/**"."/images/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// Custom exception handling
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler)
/ / permission
.and()
.authorizeRequests()
// One is that the identity information must be checked but the permission is not verified.
.antMatchers("/"."/mock/login").permitAll()
// Only anonymous access is allowed
.antMatchers("/hello").anonymous()
.anyRequest()
.authenticated()
// Form login
// .and()
// .formLogin()
// .successHandler(authenticationSuccessHandler)
// .failureHandler(authenticationFailureHandler)
// .loginProcessingUrl("/login")
// .permitAll()
/ / logout
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(logoutSuccessHandler)
.permitAll()
// Turning CSRF off generates a cSRF_Token on the page
.and()
.csrf()
.disable()
// Token-based, so no session is required
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// Add our JWT filter
.and()
.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)
;
}
@Bean
public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(a){
return new JwtAuthenticationTokenFilter();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean(a) throws Exception {
return super.authenticationManagerBean();
}
@Bean
public JwtTokenUtil jwtTokenUtil(a) {
return newJwtTokenUtil(); }}Copy the code
Create a JWT filter that inherits OncePerRequestFilter
The mall is macro’s biggest shopping mall
package top.ryzeyang.demo.common.filter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import top.ryzeyang.demo.utils.JwtTokenUtil;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/** * JWT logon authorization filter **@author macro
* @date2018/4/26 * /
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Qualifier("users")
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader(this.tokenHeader);
if(authHeader ! =null && authHeader.startsWith(this.tokenHead)) {
String authToken = authHeader.substring(this.tokenHead.length());// The part after "Bearer "
String username = jwtTokenUtil.getUserNameFromToken(authToken);
log.info("checking username:{}", username);
if(username ! =null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
log.info("authenticated user:{}", username); SecurityContextHolder.getContext().setAuthentication(authentication); } } } chain.doFilter(request, response); }}Copy the code
The custom handler
There are two Hanlers, one with insufficient permission and one with failed authentication
Insufficient access to implement AccessDeniedHandler
package top.ryzeyang.demo.common.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import top.ryzeyang.demo.common.api.CommonResult;
import top.ryzeyang.demo.common.api.ResultEnum;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Component
public class MyAccessDeineHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setContentType("application/json; charset=utf-8");
PrintWriter writer = httpServletResponse.getWriter();
writer.write(new ObjectMapper().writeValueAsString(newCommonResult<>(ResultEnum.ACCESS_ERROR, e.getMessage()))); writer.flush(); writer.close(); }}Copy the code
Authentication failed to implement AuthenticationEntryPoint
For example, the account password is incorrect
package top.ryzeyang.demo.common.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import top.ryzeyang.demo.common.api.CommonResult;
import top.ryzeyang.demo.common.api.ResultEnum;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setContentType("application/json; charset=utf-8");
PrintWriter writer = httpServletResponse.getWriter();
// Authentication failed
writer.write(new ObjectMapper().writeValueAsString(newCommonResult<>(ResultEnum.AUTHENTICATION_ERROR, e.getMessage()))); writer.flush(); writer.close(); }}Copy the code
JWT tools
The mall is macro’s biggest shopping mall
It’s changed a bit, because JDK11 uses a different version of JJWT and has a slightly different syntax
JJWT is introduced in POM files
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <! -- or jjwt-gson if Gson is preferred -->
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
Copy the code
JwtTokenUtil
package top.ryzeyang.demo.utils;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
* {" ALG ": "HS512"," TYp ": * {" alG ": "HS512"," TYp ": {"sub":"wang","created":1489079981393,"exp":1489684781} * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) * *@author macro
* @date2018/4/26 * /
@Slf4j
public class JwtTokenUtil {
private static final String CLAIM_KEY_USERNAME = "sub";
private static final String CLAIM_KEY_CREATED = "created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
@Value("${jwt.tokenHead}")
private String tokenHead;
private SecretKey getSecretKey(a) {
byte[] encodeKey = Decoders.BASE64.decode(secret);
return Keys.hmacShaKeyFor(encodeKey);
}
/** * based on the token responsible for generating the JWT */
private String generateToken(Map<String, Object> claims) {
SecretKey secretKey = getSecretKey();
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(secretKey)
.compact();
}
/** * Test the generated token *@param claims
* @return* /
public String generateToken2(Map<String, Object> claims) {
SecretKey secretKey = getSecretKey();
return Jwts.builder()
.setClaims(claims)
.setIssuer("Java4ye")
.setExpiration(new Date(System.currentTimeMillis() + 1 * 1000))
.signWith(secretKey)
.compact();
}
/** * get the payload in JWT from token */
private Claims getClaimsFromToken(String token) {
SecretKey secretKey = getSecretKey();
Claims claims = null;
try {
claims = Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
log.info("JWT format validation failed :{}", token);
}
return claims;
}
/** * Expiration time of token generation */
private Date generateExpirationDate(a) {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
/** * Obtain the login user name from the token */
public String getUserNameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
/** * Verify that the token is still valid **@paramToken Indicates the token * passed by the client@paramUserDetails User information queried from the database */
public boolean validateToken(String token, UserDetails userDetails) {
String username = getUserNameFromToken(token);
returnusername.equals(userDetails.getUsername()) && ! isTokenExpired(token); }/** * Check whether the token is invalid */
private boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFromToken(token);
return expiredDate.before(new Date());
}
/** * Get expiration time from token */
private Date getExpiredDateFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims.getExpiration();
}
/** * Generate token */ based on user information
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
/** * The token can be refreshed when the original token has not expired@paramOldToken Token with tokenHead */
public String refreshHeadToken(String oldToken) {
if (StrUtil.isEmpty(oldToken)) {
return null;
}
String token = oldToken.substring(tokenHead.length());
if (StrUtil.isEmpty(token)) {
return null;
}
// The token verification fails
Claims claims = getClaimsFromToken(token);
if (claims == null) {
return null;
}
// If the token has expired, it cannot be refreshed
if (isTokenExpired(token)) {
return null;
}
// If the token is refreshed within 30 minutes, the original token is returned
if (tokenRefreshJustBefore(token, 30 * 60)) {
return token;
} else {
claims.put(CLAIM_KEY_CREATED, new Date());
returngenerateToken(claims); }}/** * Determines whether the token has just been refreshed within the specified time@paramToken the original token *@paramTime Specifies the time (s) */
private boolean tokenRefreshJustBefore(String token, int time) {
Claims claims = getClaimsFromToken(token);
Date created = claims.get(CLAIM_KEY_CREATED, Date.class);
Date refreshDate = new Date();
// Refresh time within the specified time of creation time
if (refreshDate.after(created) && refreshDate.before(DateUtil.offsetSecond(created, time))) {
return true;
}
return false; }}Copy the code
Configuration file application.yml
The secret here can be generated this way
@Test
void generateKey(a) {
/** * Key Key = key.secretKeyfor (signaturealgorithm.hs256); * String secretString = Encoders.BASE64.encode(key.getEncoded()); This article uses BASE64 encoding * */
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
String secretString = Encoders.BASE64.encode(key.getEncoded());
System.out.println(secretString);
// Blk1X8JlN4XH4s+Kuc0YUFXv+feyTgVUMycSiKbiL0YhRddy872mCNZBGZIb57Jn2V1RtaFXIxs8TvNPsnG//g==
}
Copy the code
jwt:
tokenHeader: Authorization
secret: Blk1X8JlN4XH4s+Kuc0YUFXv+feyTgVUMycSiKbiL0YhRddy872mCNZBGZIb57Jn2V1RtaFXIxs8TvNPsnG//g==
expiration: 604800
tokenHead: 'Bearer '
server:
servlet:
context-path: /api
Copy the code
Controller
AuthController
package top.ryzeyang.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.*;
import top.ryzeyang.demo.common.api.CommonResult;
import top.ryzeyang.demo.model.dto.UserDTO;
import top.ryzeyang.demo.utils.CommonResultUtil;
import top.ryzeyang.demo.utils.JwtTokenUtil;
import java.util.Collection;
@RestController
@ResponseBody
@RequestMapping("/mock")
public class AuthController {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Qualifier("users")
@Autowired
private UserDetailsService userDetailsService;
@Autowired
AuthenticationManager authenticationManager;
@GetMapping("/userinfo")
public CommonResult<Collection<? extends GrantedAuthority>> getUserInfo(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
return CommonResultUtil.success(authorities);
}
/** * log in */
@PostMapping("/login")
public CommonResult<String> login(@RequestBody UserDTO userDTO){
String username = userDTO.getUsername();
String password = userDTO.getPassword();
UsernamePasswordAuthenticationToken token
= new UsernamePasswordAuthenticationToken(username, password);
Authentication authenticate = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authenticate);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
String t = jwtTokenUtil.generateToken(userDetails);
returnCommonResultUtil.success(t); }}Copy the code
HelloController
package top.ryzeyang.demo.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(a) {
return "hello";
}
@GetMapping("/hello/anonymous")
public String hello2(a) {
return "anonymous";
}
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/hello/admin")
public String helloAdmin(a) {
return "hello Admin";
}
@PreAuthorize("hasRole('USER')")
@GetMapping("/hello/user")
public String helloUser(a) {
return "hello user";
}
@PreAuthorize("hasAnyAuthority('system:user:query')")
@GetMapping("/hello/user2")
public String helloUser2(a) {
return "hello user2"; }}Copy the code
The project is on GitHub
Springsecurity-vuetify-permissions -demo: github.com/RyzeYang/Sp…
I can’t preview it online now
Combined with the front end, it looks like 😄
Java4ye A little white blogger focused on productivity ~(increasing groping time), sharing learning resources, technological awareness, little things about programmer life let us groping together ~(● world twentyconsciousness) Java4ye here to prepare a series of learning resources for you, as well as a variety of plug-ins, software oh welcome message! Thanks for your support! O (≧ ヾ del ≦ *)