04 choice of spring Security + JWT + redis login scheme -- login from 0 to 1

Kojiro, a hothead 2020-11-08 09:41:41
choice spring security jwt redis


Preface

The author from hallo world Starting at the current rookie level , Almost every project I've been exposed to can't avoid this function . From the beginning, I didn't understand the conversation mechanism , If the password account is verified successfully, just go to session Personal information in it , To the later understanding of stateless 、 Stateful login programs , It has to be said that learning is a process of accumulation , Want to understand a technology completely , A technology stack , You have to learn systematically . I think of a little brother who interviewed for two years before , I don't know what I think , He came at the end of his resume 4 A pie chart , Show your mastery in percentage form , I couldn't help laughing ( Of course not during the interview )——java 75%,mysql80%,orcle75%,linux80%, You've seen hashmap Source code , Understand the underlying principles ? forehead ~... No, , I do not know! . So you used triggers , Do you know what stored procedures are ? forehead ~... No use. ...

In fact, this kind of situation is very common , Some young job seekers use rough resumes when looking for jobs , Including the author's own . But be sure to pay attention to the technology stack that you will , You must use the right key words to the degree you master . For example, one year of technical experience java Development , It's written on your resume 、 Have a good command of springboot, A slightly better company should just brush you off , Companies that are short of people may make an appointment for an interview , But interviewers are generally disgusted . Of course, if you're gifted , Can really achieve the degree of mastery in such a short time , Then I have nothing to say , It is suggested that we should direct aliP8.

1、 The choice of plan

There are many ways to log in to identity storage , But many projects need to consider the security and authority system of the project , Now the popular authority framework is shiro, and springsecurity, The former is larger than the user group . There are two categories of logins as a whole : Stateless and stateful .

  • A stateful :

    Stateful means that the user has a conversation with the server , Use session And so on , The server will save the user's session information .

  • No state :

    And stateless is the more popular way now , Popular said , The server doesn't keep information about your user session , After the user visits and passes the authentication authorization to you token, Stored in the client , The server will token Analyze the information in it , Then according to this information to do business operations . In parsing token Before , I don't know who you are , The server does not keep token, Don't use session, In the case of a large number of visits, the server pressure will be reduced .

Therefore, the choice of login scheme mainly depends on the specific project scale , Business needs .

this paper ( This project ) The main introduction is Spring security combination jwt、redis To log in .

Because of business needs , On the basis of no state , take token The only way to deal with it , It means single sign on , single token Deal with... Effectively , use redis To achieve . therefore , Break the no state . Mainly because of the single jwt There are some disadvantages to the authentication method of :

  1. token Cannot force invalidation after generation , Can only automatically expire ;
  2. token Can't delay ;
  3. Unable to synchronize after modifying user information ;
  4. token Can't store too much information ( The author once put an entire object entity , Including all object permissions, the object resolves to token, result request newspaper token It's too long to be used );
  5. Based on the first point, there is a case that the user can log in successfully after multiple logins , It means that the server will issue it many times token, And these token It's all valid ;

2、Spring security brief introduction

spring security Its core functions mainly include :

  • authentication ( who are you )
  • to grant authorization ( What can you do? )
  • Attack protection ( Prevent identity forgery )

spring security You can talk to shiro As well as the control of permissions , And compared with shiro It will be more powerful .Spring Security It can be for Spring Applications provide declarative security access control , Through a series of spring Configurable in the application context bean, utilize spring ioc and aop And other functional features to provide declarative security access control functions ( Control access to interfaces and methods through annotations ), Reduce repetitive work .

3、jwt

JSON Web Token (JWT), Is a kind of information transmission between network applications based on JSON Open standards for , user JSON Objects are used to transmit information safely in different systems , The main usage scenario is used to transfer authenticated user identity information between user identity provider and service provider .

about Spring security and jwt This article will not do too much to introduce , Focus on achieving .

4、 Start implementation

1) pom Import :

<!-- Here we show only security And jwt -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>

2) Start profile :

SecurityConfig:

package com.ssk.shop.config.security;
​
import com.ssk.shop.config.security.token.JwtTokenUtil;
import com.ssk.shop.config.security.token.RestAuthenticationEntryPoint;
import com.ssk.shop.config.security.token.RestfulAccessDeniedHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.firewall.DefaultHttpFirewall;
import org.springframework.security.web.firewall.HttpFirewall;
​
import javax.sql.DataSource;
​
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
​
​
@Autowired
private BCryptPasswordEncoder passwordEncoder;
​
/**
* Register processors that don't have permissions
*/
@Bean
public RestfulAccessDeniedHandler restfulAccessDeniedHandler() {
return new RestfulAccessDeniedHandler();
}
​
@Bean
public RestAuthenticationEntryPoint restAuthenticationEntryPoint() {
return new RestAuthenticationEntryPoint();
}
​
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
​
// The source of the authenticated user ( Memory or database )
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder);
}
​
// To configure springSecurity Related information artisanType/get_all
protected void configure(HttpSecurity http) throws Exception {
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
// Resource paths that do not need to be protected allow access
for (String url : ignoreUrlsConfig().getUrls()) {
registry.antMatchers(url).permitAll();
}
// Allow cross domain requests for OPTIONS request
registry.antMatchers(HttpMethod.OPTIONS)
.permitAll();
// Any request requires authentication
// Release static resources , Specify resource interception rules , Specify the custom authentication page , Specify exit authentication configuration ,csrf To configure
registry.and()
.authorizeRequests()
.anyRequest()
.authenticated()
// Turn off cross station request protection and not use session
.and()
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// Custom permission denied processing class
.and()
.exceptionHandling()
.accessDeniedHandler(restfulAccessDeniedHandler())
.authenticationEntryPoint(restAuthenticationEntryPoint())
// Custom permission interceptor JWT filter
.and()
.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
​
@Autowired
DataSource dataSource;
​
@Bean
public PersistentTokenRepository getPersistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
//       jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
​
@Bean
public IgnoreUrlsConfig ignoreUrlsConfig() {
return new IgnoreUrlsConfig();
}
​
@Bean
public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
return new JwtAuthenticationTokenFilter();
}
​
@Bean
public JwtTokenUtil jwtTokenUtil() {
return new JwtTokenUtil();
}
​
@Bean
public HttpFirewall httpFirewall() {
return new DefaultHttpFirewall();
}
​
}

JwtTokenUtil:

package com.ssk.shop.config.security.token;
​
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
​
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
​
/**
* jwt token Generation tool
*
* @author ssk
* @since 2020-11-03 08:09:00
*/
public class JwtTokenUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
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;
​
/**
* Generate according to responsibility JWT Of token
*/
private String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
​
/**
* from token In order to get JWT Load in
*/
private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
LOGGER.info("JWT Format validation failed :{}", token);
}
return claims;
}
​
/**
* Generate token The expiration time of
*/
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
​
/**
* from token Get login user name
*/
public String getUserNameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
​
/**
* verification token Is it still valid
*
* @param token       From the client token
* @param userDetails User information from the database
*/
public boolean validateToken(String token, UserDetails userDetails) {
String username = getUserNameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
​
/**
* Judge token Is it invalid
*/
private boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFromToken(token);
return expiredDate.before(new Date());
}
​
/**
* from token Get the expiration time in the
*/
private Date getExpiredDateFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims.getExpiration();
}
​
/**
* According to the user information token
*/
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);
}
​
/**
* When the original token It can be refreshed when it is not expired
*
* @param oldToken belt tokenHead Of token
*/
public String refreshHeadToken(String oldToken) {
if(StrUtil.isEmpty(oldToken)){
return null;
}
String token = oldToken.substring(tokenHead.length());
if(StrUtil.isEmpty(token)){
return null;
}
//token Verification failed
Claims claims = getClaimsFromToken(token);
if(claims==null){
return null;
}
// If token It's overdue , Refresh is not supported
if(isTokenExpired(token)){
return null;
}
// If token stay 30 Just refreshed in minutes , Go back to the original token
if(tokenRefreshJustBefore(token,30*60)){
return token;
}else{
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
}
​
/**
* Judge token Whether it has just been refreshed in the specified time
* @param token primary token
* @param time Specify time ( second )
*/
private boolean tokenRefreshJustBefore(String token, int time) {
Claims claims = getClaimsFromToken(token);
Date created = claims.get(CLAIM_KEY_CREATED, Date.class);
Date refreshDate = new Date();
// The refresh time is within the specified time of creation time
if(refreshDate.after(created)&&refreshDate.before(DateUtil.offsetSecond(created,time))){
return true;
}
return false;
}
}

jwt filter :

package com.ssk.shop.config.security;
​
import com.ssk.shop.config.security.token.JwtTokenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.web.filter.OncePerRequestFilter;
​
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
​
/**
* jwt filter
*
* @author ssk
* @since 2020-11-03 08:09:00
*/
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
@Resource
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);
LOGGER.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));
LOGGER.info("authenticated user:{}", username);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
chain.doFilter(request, response);
}
}

If you are using a single project and multiple types of users , And they all want to have login control ,(admin There may be a lot of roles and permissions involved , And the client doesn't design , It's a single user role ) Can be admin and user( Other users ) inherit security Of UserDetailsService, And then after logging in UserDetail Re encapsulate , It's better to put in characters , then jwt The verification is based on id take out Userdetail The role in the information is used to re authorize the corresponding layer , The sample code :

// Will inherit UserDetail The class of the
@Resource
private IAdminInfoFacade adminDetailsService;
@Resource
private IUserInfoFacade userDetailsFacade;
​
​
​
@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 "
if (jwtTokenUtil.validateTokenSingle(authToken)){
String username = jwtTokenUtil.getUserNameFromToken(authToken);
// according to token After parsing, get the character ADMIN/USER
String role = jwtTokenUtil.getUserRoleFromToken(authToken);
LOGGER.info("checking username:{}", username);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = null;
// More role to choose which implementation class to enter for reauthorization verification
if (role.equals(UserRoleTypeEnum.ADMIN.getType()) || role.equals(UserRoleTypeEnum.SUPER.getType())){
userDetails = this.adminDetailsService.loadUserByUsername(username);
}else if (role.equals(UserRoleTypeEnum.USER.getType())){
userDetails = this.userDetailsFacade.loadUserByUsername(username);
}
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
LOGGER.info("authenticated user:{}", username);
SecurityContextHolder.getContext().setAuthentication(authentication);
System.out.println("//");
}
}
}
}

But this method is not recommended to use security Your own login , Instead, each role is used to implement the login interface .

Custom unauthorized return :

package com.ssk.shop.config.security.token;
​
import cn.hutool.json.JSONUtil;
import com.ssk.utils.CommonResult;
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;
​
/**
* Custom return results : When you don't have permission to access
* @author ssk
* @since 2020-11-03 08:08:40
*/
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException e) throws IOException, ServletException {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Cache-Control","no-cache");
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println(JSONUtil.parse(CommonResult.forbidden(e.getMessage())));
response.getWriter().flush();
}
}

Custom not logged in / Unauthenticated return :

package com.ssk.shop.config.security.token;
​
import cn.hutool.json.JSONUtil;
import com.ssk.utils.CommonResult;
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;
​
/**
* Custom return results : Not signed in or sign in expired
* @author ssk
* @since 2020-11-03 08:08:40
*/
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Cache-Control","no-cache");
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println(JSONUtil.parse(CommonResult.unauthorized(authException.getMessage())));
response.getWriter().flush();
}
}

jwt The basic configuration of

jwt:
tokenHeader: Authorization #JWT Stored request header
secret: jwt_key #JWT The key used for encryption and decryption
expiration: 604800 #JWT Beyond the deadline of (60*60*24)
tokenHead: Bearer  #JWT Get the beginning of the load
#security No authorized release list Need to combine the above security The configuration file
secure:
ignored:
urls: # Safe path white list
- /swagger-ui.html
- /swagger-resources/**
- /swagger/**
- /**/v2/api-docs
- /webjars/springfox-swagger-ui/**
- /actuator/**
- /druid/**
- /admin-info/login
- /admin/register
- /admin/info
- /admin/logout

That's all the basic configuration ,jwt I put the key and header directly into the configuration file .

5、 Log on to the test

image

Use here swagger Test directly , The login method used is to rewrite it yourself

@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
​
@ApiOperation(value = " Log in and return to token")
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ResponseBody
public CommonResult<Map<String, String>> login(@RequestBody UserLoginVO user) {
try {
String token = adminInfoFacade.login(user.getUsername(), user.getPassword());
Map<String, String> tokenMap = new HashMap<>();
tokenMap.put("token", token);
tokenMap.put("tokenHead", tokenHead);
return CommonResult.success(tokenMap);
}catch (ServiceException e){
return CommonResult.failed(e.getMessage());
}
}

take security Of loadUserByUsername Rewrite , Then customize a login class LoginMan Used to save account information :

private PasswordEncoder passwordEncoder;
​
@Resource
private JwtTokenUtil jwtTokenUtil;
​
@Override
public String login(String username, String password) {
String token = null;
try {
LoginMan userDetails = this.loadUserByUsername(username);
if(!passwordEncoder.matches(password,userDetails.getPassword())){
throw new BadCredentialsException(" Incorrect password ");
}
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
token = jwtTokenUtil.generateToken(userDetails);
} catch (AuthenticationException e) {
LOGGER.warn(" Login exception :{}", e.getMessage());
throw new ServiceException(" Wrong user name or password ");
}
return token;
}

LoginMan: Adapt to your own business needs , The main implementation of the corresponding interface :

package com.ssk.shop.dto;
​
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
​
import java.util.ArrayList;
import java.util.List;
​
/**
* Login user entity
*
* @author ssk
* @since 2020-11-03 08:09:00
*/
public class LoginMan implements GrantedAuthority, UserDetails {
/**
* Entity id
*/
private String id;
​
​
/**
* password
*/
private String password;
​
private Object manMsg;
​
private String roleType;
​
/**
* name
*/
private String name;
​
private List<String> permissions;
​
private boolean enabled = false;
​
public Object getManMsg() {
return manMsg;
}
​
public void setManMsg(Object manMsg) {
this.manMsg = manMsg;
}
​
@Override
@JsonIgnore
public boolean isAccountNonExpired() { // Is the account overdue
return true;
}
​
@Override
@JsonIgnore
public boolean isAccountNonLocked() { // Whether the account is frozen
return true;
}
​
// Whether the account password has expired , Generally, some systems with high password requirements will use , The comparison requires the user to reset the password every once in a while
@Override
@JsonIgnore
public boolean isCredentialsNonExpired() {
return true;
}
​
@Override
public boolean isEnabled() {
return enabled;
}
​
public void setPassword(String password) {
this.password = password;
}
​
public String getName() {
return name;
}
​
public void setName(String name) {
this.name = name;
}
​
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
​
public String getRoleType() {
return roleType;
}
​
public void setRoleType(String roleType) {
this.roleType = roleType;
}
​
public List<String> getPermissions() {
return permissions;
}
​
public String getId() {
return id;
}
​
public void setId(String id) {
this.id = id;
}
​
​
@Override
@JsonIgnore
public List<GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_" + roleType));
for (String permission : permissions) {
authorities.add(new SimpleGrantedAuthority(permission));
}
return authorities;
}
​
@Override
public String getPassword() {
return password;
}
​
@Override
public String getUsername() {
return name;
}
​
@Override
@JsonIgnore
public String getAuthority() {
return null;
}
​
public LoginMan(String name, String password, String roleType, List<String> permissions, String id , Object manMsg) {
this.name = name;
this.password = password;
this.roleType = roleType;
this.permissions = permissions;
this.id = id;
this.manMsg = manMsg;
}
​
public LoginMan() {
​
}
​
@Override
public String toString() {
return "LoginMan{" +
"id='" + id + ''' +
", password='" + password + ''' +
", manMsg=" + manMsg +
", roleType='" + roleType + ''' +
", name='" + name + ''' +
", permissions=" + permissions +
", enabled=" + enabled +
'}';
}
}

Sign in :

# success
{
"code": 200,
"message": " Successful operation ",
"data": {
"tokenHead": "Bearer",
"token": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJmcmlkYXkiLCJjcmVhdGVkIjoxNjA0NzA5NzkyMjI2LCJleHAiOjE2MDUzMTQ1OTJ9.EHoNsN1W1Q3sek64XGV0ubIXhnTBpcvjAezbzDOLQAhQnCzvO9f4ovCJUvtrLyzSexqvJcoOR8ccBEPd6HXFOw"
}
}

# Failure
{
"code": 500,
"message": " Wrong user name or password ",
"data": null
}

image

And then token to swagger overall situation (token You need to add a head , And use space between and value );

Access the interface that needs permission :

@PreAuthorize("hasAuthority('ADMIN_SEE')")
@RequestMapping("/query_page")
@ApiOperation(value = " Query administrator collection ", notes = " Query administrator collection ", httpMethod = "GET")
@ApiImplicitParams({
@ApiImplicitParam(name = "roleId", value = " role ID", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "account", value = " account number ", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "name", value = " name ", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "sex", value = " Gender (0: male ,1: Woman )", paramType = "query", dataType = "int"),
@ApiImplicitParam(name = "enable", value = " state (1 Yes enable ,0 Is disabled ))", paramType = "query", dataType = "int"),
@ApiImplicitParam(name = "phone", value = " Telephone ", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "page", value = " The number of pages displayed ", required = true, paramType = "query", dataType = "int",example = "1"),
@ApiImplicitParam(name = "pageSize", value = " Each page shows several ", required = true, paramType = "query", dataType = "int",example = "10")
})
public CommonResult<CommonPage<AdminListDto>> selectAdmin(@RequestParam(required = false) String roleId,
@RequestParam(required = false) String account,
@RequestParam(required = false) String name,
@RequestParam(required = false) Integer sex,
@RequestParam(required = false) Integer enable,
@RequestParam(required = false) String phone,
Integer page, Integer pageSize) {
try {
return CommonResult.success(adminInfoFacade.selectAdmin(roleId, account, name, sex, enable, phone,  page, pageSize));
} catch (ServiceException e) {
e.printStackTrace();
return CommonResult.failed(e.getMessage());
}
}

Successfully obtained the return value :

{
"code": 200,
"message": " Successful operation ",
"data": {
"total": 0,
"size": 10,
"pages": 0,
"current": 1,
"records": [
{
"adminInfoId": "053d9349b5ca4c56a873053c2afbc6df",
"account": "bjty",
"password": "$2a$10$jSw6APBmhUzkNuSVFKEGR.yu/srrbwHUimpvllm1QZB9ifMf1wXry",
"adminRole": "fcc5a1585f5b4e32bc283a3e232435c0",
"roleName": " Administrators 2 Number ",
"adminDepartmentName": null,
"name": " Plain boiled water ",
"sex": null,
"phone": "18999999999",
"enableState": 1,
"createTime": "2020-08-25T02:28:47.000+0000"
},

Empty token After the interview :

{
"code": 401,
"data": "Full authentication is required to access this resource",
"message": " Not logged in or token It's overdue "
}

summary :

The initial setting in the project is that the back-end administrator can have a single role , Then classify the roles according to their permissions , Interface access is controlled by permissions or roles . The personal understanding is security Put the account permission and role in login session( enabled ), And then access through the session session Conversation information in , To get the authority to compare , When you are satisfied, you can release the visit .

security adopt aop To verify the authority of the system , Compare permissions through annotations

@PreAuthorize("hasAnyRole('ROLE_ADMIN')")// role
@PreAuthorize("hasAuthority('ADMIN_SEE')")// jurisdiction 

For roles , We need to initialize the role at login, such as add when permission set “ROLE_”, Used to identify characters .

public LoginMan(String name, String password, String roleType, List<String> permissions, String id , Object manMsg) {
this.name = name;
this.password = password;
this.roleType = roleType;
this.permissions = permissions;
this.id = id;
this.manMsg = manMsg;
}
​
@Override
@JsonIgnore
public List<GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_" + roleType));
for (String permission : permissions) {
authorities.add(new SimpleGrantedAuthority(permission));
}
return authorities;
}

This article instantiates LoginMan(UserDetail) The role type is passed in as a parameter .

If not disabled session, You can also get it in the following ways session The value in :

/**
* Get the current account id
* @return
* @throws ServiceException
*/
public String getThisAccountId()throws ServiceException {
try {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (ObjectHelper.isNotEmpty(principal) && principal.equals("anonymousUser")){
return null;
}else {
LoginMan thisMan = (LoginMan) principal;
return thisMan.getId();
}
}catch (Exception e){
return null;
}
}

It will be disabled later session, use token Save the way to achieve .

版权声明
本文为[Kojiro, a hothead]所创,转载请带上原文链接,感谢

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云