This is the seventh day of my participation in the More text Challenge. For details, see more text Challenge
preface
This article focuses on how to integrate SpringSecurity into SpringBoot
Past the link
- Brief account main function introduction
- Brief introduction and deployment of the accounting backend environment
- Brief introduction and deployment of account front end environment
- Solve the small program scan code authorization prompt Token cannot be empty
- Database design
- Resolve AOP logging errors
- Brief Spring Security
I. Development environment
The development environment is as follows:
- Java: its 11
- Spring Security: 5.3.3
Integrate Spring Security
This section uses SpringSecurity + JWT as an example
1. Introduce dependencies
Spring-security does not need to specify a version, it is specified in the parent POM. The JJWT library also needs to be introduced because JWT is required
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<! -- JJWT is a Java library that provides end-to-end JWT creation and validation -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
Copy the code
2. Configure Spring Security
The master configuration
The main configuration declares the requests that need to be intercepted and defines the Filter
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(a){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
/ * * * anyRequest | match all the access request path * | SpringEl expression results when they can access to true * anonymous | anonymous access to * denyAll | users cannot access * FullyAuthenticated | user authentication can access fully (not remember - automatic login under me) * hasAnyAuthority | if you have parameters, parametric representation rights, have access any one can access * hasAnyRole | if there is a parameter, Parameters according to role, then any one role can access * hasAuthority | if you have parameters, parameter access, is it can access * hasIpAddress | if you have parameters, parameter indicates the IP address, if the user IP and matching parameters, Can access * hasRole | if you have parameters, parameter, Its role can access * permitAll | users can access any * rememberMe | allow authenticated by remember - me login user access * | accessible * / after the user login
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// No session is required
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/user/register")
.antMatchers("/doc.html")
.antMatchers("/webjars/**")
.antMatchers("/swagger-resources/**")
.antMatchers("/v2/**")
.antMatchers("/favicon.ico")
// the Url obtained by wechat openId is not intercepted
.antMatchers("/wx/openId/*")
.antMatchers("/wx/login")
// The login qr code is not blocked
.antMatchers("/qrcode/**");
web.expressionHandler(new DefaultWebSecurityExpressionHandler() {
@Override
protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) {
WebSecurityExpressionRoot root = (WebSecurityExpressionRoot) super.createSecurityExpressionRoot(authentication, fi);
root.setDefaultRolePrefix(""); // remove the prefix ROLE_
returnroot; }}); }@Bean
CorsConfigurationSource corsConfigurationSource(a) {
return req -> {
CorsConfiguration cfg = new CorsConfiguration();
cfg.addAllowedHeader("*");
cfg.addAllowedMethod("*");
cfg.addAllowedOrigin("*");
cfg.setAllowCredentials(true);
cfg.checkOrigin("*");
returncfg; }; }}Copy the code
The authentication
The main thing in the authentication filter is to get the user name and password from the request and give it to the AuthenticationManager
@Slf4j
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
super.setFilterProcessesUrl("/user/login");
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
// Get the login information from the input stream
try {
LoginVO loginVO = new ObjectMapper().readValue(request.getInputStream(), LoginVO.class);
return authenticationManager.authenticate(
// The password must be encrypted for authentication
new UsernamePasswordAuthenticationToken(loginVO.getUsername(), loginVO.getPassword())
);
} catch (IOException e) {
e.printStackTrace();
return null; }}/** * Successfully called method */
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException {
JWTUser jwtUser = (JWTUser) authResult.getPrincipal();
log.info("JWTUser:{}",jwtUser.toString());
List<String> permissionList = new ArrayList<>();
jwtUser.getAuthorities().forEach(n -> permissionList.add(n.getAuthority()));
// Get the JWTConfig object by getting the Spring context
JWTConfig jwtConfig = SpringContextUtil.getBean(JWTConfig.class);
String token = jwtConfig.createToken(jwtUser.getId().toString(),permissionList);
UserService userService = SpringContextUtil.getBean(UserService.class);
UserDO userDO = userService.getById(jwtUser.getId());
LoginSuccessVO vo = new LoginSuccessVO();
BeanUtils.copyProperties(userDO, vo);
vo.setToken(token);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
response.getWriter().write(JSON.toJSONString(Result.success(vo)));
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8"); response.getWriter().write(JSON.toJSONString(Result.error(CodeMsg.LOGIN_ERROR), SerializerFeature.WriteMapNullValue)); }}Copy the code
authorization
Authorization filters do one thing: set Authentication in the context of the SecurityContext
@Slf4j
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String token = request.getHeader("token");
// Verify the token correctness
try {
checkToken(token);
// If there is a token in the request header, it is resolved and authentication information is set
SecurityContextHolder.getContext().setAuthentication(getAuthentication(token));
super.doFilter(request, response, chain);
} catch (BaseException e) {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8"); response.getWriter().write(JSON.toJSONString(Result.error(e.getCodeMsg(), e.getMessage()), SerializerFeature.WriteMapNullValue)); }}// Get the user information from the token and create a token
private UsernamePasswordAuthenticationToken getAuthentication(String token) {
JWTConfig jwtConfig = SpringContextUtil.getBean(JWTConfig.class);
Claims claims = jwtConfig.getTokenClaim(token);
if(claims ! =null){
String userId = jwtConfig.getUserIdFromToken(claims);
// Set current user Id to thread
LocalUserId.set(Long.valueOf(userId));
List<String> permissions = jwtConfig.getPermissions(claims);
List<GrantedAuthority> list = new ArrayList<>();
permissions.forEach(
n-> list.add(new SimpleGrantedAuthority(n))
);
return new UsernamePasswordAuthenticationToken(userId, null, list);
}else {
log.error("Token format is incorrect");
throw newBusinessException(CodeMsg.JWT_EXCEPTION); }}/** * Verify token correctness */
private void checkToken(String token) {
JWTConfig jwtConfig = SpringContextUtil.getBean(JWTConfig.class);
if(StringUtils.isEmpty(token)){
log.error("{} cannot be empty",jwtConfig.getHeader());
throw new BusinessException(CodeMsg.JWT_EXCEPTION);
}
Claims claims = jwtConfig.getTokenClaim(token);
if(claims == null){
log.error(CodeMsg.JWT_EXCEPTION.getMessage());
throw new BusinessException(CodeMsg.JWT_EXCEPTION);
}
if (jwtConfig.isTokenExpired(claims)) {
log.error(CodeMsg.TOKEN_EXPIRED.getMessage());
throw newParameterException(CodeMsg.TOKEN_EXPIRED); }}}Copy the code
3. Write permission interfaces
Here to charge to an account in the analysis of an interface as an example the @ PreAuthorize (” hasAuthority (‘ record: analysis: spendCategoryTotal ‘) “) on behalf of the current user must have the permission to characters
@LoginRequired
@commonLog (title = "Get total of all consumer categories for a year ", businessType = Businesstype.query)
@apiOperation (value = "get the total of all consumption categories for a year ")
@PreAuthorize("hasAuthority('record:analysis:spendCategoryTotal')")
@GetMapping("/spendCategoryTotal/{year}/{recordType}")
publicResult<? > getSpendCategoryTotalInYear(@apiparam (required = true, value = "yyyy ") @Validated
@DateTimeFormat(pattern="yyyy") @PathVariable(value = "year") Date date,
@notnull (Message = "Billing type code cannot be empty ") @PathVariable(value = "recordType")String recordType) {
if(! recordType.equals(RecordConstant.EXPEND_RECORD_TYPE) && ! recordType.equals(RecordConstant.INCOME_RECORD_TYPE)) {return Result.error(CodeMsg.RECORD_TYPE_CODE_ERROR);
}
UserDO userDO = LocalUser.get();
List<SpendCategoryTotalDTO> list = recordDetailService.getSpendSpendCategoryTotalByYear(userDO.getId(), recordType,
date);
return Result.success(list);
}
Copy the code
4. Test
The results can be successfully obtained
Third, summary
The above code can be found in the ledger back end
Thank you for seeing the end, it is my great honor to help you ~♥