Microservice architecture
- Gateway: route the user's request to the specified service and forward the Session information contained in the front-end Cookie;
- User service: user login Authentication, user authorization, and Redis Session Management
- Other services: rely on user information in Redis for interface request verification
Structure design of user role permission table
-
Permission table
The minimum granularity of permission table controls a single function, such as user management, resource management, table structure example:
id | authority | description |
---|---|---|
1 | ROLE_ADMIN_USER | Manage all users |
2 | ROLE_ADMIN_RESOURCE | Manage all resources |
3 | ROLE_A_1 | Access to an interface of ServiceA |
4 | ROLE_A_2 | Access to another interface of ServiceA |
5 | ROLE_B_1 | Access to an interface of ServiceB |
6 | ROLE_B_2 | Access to another interface of ServiceB |
-
Role permission table
Customize roles and combine various permissions. For example, super administrator has all permissions. Table structure example:
id | name | authority_ids |
---|---|---|
1 | Super administrator | 1,2,3,4,5,6 |
2 | Administrator A | 3,4 |
3 | Administrator B | 5,6 |
4 | Ordinary users | NULL |
-
User role table
The user binds one or more roles, i.e. assigns various permissions. Example table structure:
user_id | role_id |
---|---|
1 | 1 |
1 | 4 |
2 | 2 |
User service design
Maven dependency (all services)
<!-- Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- Spring Session Redis --> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
Application configuration application.yml example:
# Spring Session configuration spring.session.store-type=redis server.servlet.session.persistent=true server.servlet.session.timeout=7d server.servlet.session.cookie.max-age=7d # Redis configuration spring.redis.host=<redis-host> spring.redis.port=6379 # MySQL configuration spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://<mysql-host>:3306/test spring.datasource.username=<username> spring.datasource.password=<passowrd>
authentication and authorization of user login
Slf4j public class CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter { private final UserService userService; CustomAuthenticationFilter(String defaultFilterProcessesUrl, UserService userService) { super(new AntPathRequestMatcher(defaultFilterProcessesUrl, HttpMethod.POST.name())); this.userService = userService; } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { JSONObject requestBody = getRequestBody(request); String username = requestBody.getString("username"); String password = requestBody.getString("password"); UserDO user = userService.getByUsername(username); if (user != null && validateUsernameAndPassword(username, password, user)){ // Query the user's authority List<SimpleGrantedAuthority> userAuthorities = userService.getSimpleGrantedAuthority(user.getId()); return new UsernamePasswordAuthenticationToken(user.getId(), null, userAuthorities); } throw new AuthenticationServiceException("Login failed"); } /** * Get request body */ private JSONObject getRequestBody(HttpServletRequest request) throws AuthenticationException{ try { StringBuilder stringBuilder = new StringBuilder(); InputStream inputStream = request.getInputStream(); byte[] bs = new byte[StreamUtils.BUFFER_SIZE]; int len; while ((len = inputStream.read(bs)) != -1) { stringBuilder.append(new String(bs, 0, len)); } return JSON.parseObject(stringBuilder.toString()); } catch (IOException e) { log.error("get request body error."); } throw new AuthenticationServiceException(HttpRequestStatusEnum.INVALID_REQUEST.getMessage()); } /** * Verify user name and password */ private boolean validateUsernameAndPassword(String username, String password, UserDO user) throws AuthenticationException { return username == user.getUsername() && password == user.getPassword(); } } @EnableWebSecurity @AllArgsConstructor public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private static final String LOGIN_URL = "/user/login"; private static final String LOGOUT_URL = "/user/logout"; private final UserService userService; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(LOGIN_URL).permitAll() .anyRequest().authenticated() .and() .logout().logoutUrl(LOGOUT_URL).clearAuthentication(true).permitAll() .and() .csrf().disable(); http.addFilterAt(bipAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) .rememberMe().alwaysRemember(true); } /** * Custom authentication filter */ private CustomAuthenticationFilter customAuthenticationFilter() { CustomAuthenticationFilter authenticationFilter = new CustomAuthenticationFilter(LOGIN_URL, userService); return authenticationFilter; } }
Other service design
Application configuration application.yml example:
# Spring Session configuration spring.session.store-type=redis # Redis configuration spring.redis.host=<redis-host> spring.redis.port=6379
Global security configuration
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .csrf().disable(); } }
Access to user authentication information
After the user logs in successfully through the user service, the user information will be cached to Redis. The cached information is related to the object returned by the attemptAuthentication() method in the CustomAuthenticationFilter. As mentioned above, the returned object is the new usernamepasswordauthenticationtoken (user. Getid(), null, user authorities), that is, Redis caches the user's ID and user's authority (a Hotels).
The first parameter of the UsernamePasswordAuthenticationToken constructor is the Object object Object, so you can customize the cache Object.
The method to obtain the user's information in each module of the microservice is as follows:
@GetMapping() public WebResponse test(@AuthenticationPrincipal UsernamePasswordAuthenticationToken authenticationToken){ // slightly }
Authority control
- Enable method based permission annotation
@SpringBootApplication @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
-
Simple permission verification
For example, to delete the interface of a role, only users with the role admin user permission are allowed to access it.
/** * Delete roles */ @PostMapping("/delete") @PreAuthorize("hasRole('ADMIN_USER')") public WebResponse deleteRole(@RequestBody RoleBean roleBean){ // slightly }
@Preauthorize ("hasrole ('< authority >')) can act on various modules in the microservice
-
User defined permission verification
As shown above, the hasRole() method is embedded in Spring Security. To customize it, you can use expression based access control. For example:
/** * Custom verification service */ @Service public class CustomService{ public boolean check(UsernamePasswordAuthenticationToken authenticationToken, String extraParam){ // slightly } } /** * Delete roles */ @PostMapping() @PreAuthorize("@customService.check(authentication, #userBean.username)") public WebResponse custom(@RequestBody UserBean userBean){ // slightly }
authentication belongs to the built-in object. Get the value of the input parameter.
-
Dynamic modification of any user authority
In principle, the user's permission information is saved in Redis. To modify the user's permission, you need to operate Redis. For example:
@Service @AllArgsConstructor public class HttpSessionService<S extends Session> { private final FindByIndexNameSessionRepository<S> sessionRepository; /** * Reset user rights */ public void resetAuthorities(Long userId, List<GrantedAuthority> authorities){ UsernamePasswordAuthenticationToken newToken = new UsernamePasswordAuthenticationToken(userId, null, authorities); Map<String, S> redisSessionMap = sessionRepository.findByPrincipalName(String.valueOf(userId)); redisSessionMap.values().forEach(session -> { SecurityContextImpl securityContext = session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); securityContext.setAuthentication(newToken); session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext); sessionRepository.save(session); }); } }
To modify the user permissions, you only need to call httpSessionService.resetAuthorities() method, which takes effect in real time.
Copyright belongs to the author. Please contact the author for reprint or content cooperation.
● Spring Cloud Gateway - quick start
● APM tool looks around and finds SkyWalking is my true love
● Spring Boot injects static variables from external configuration to internal application
● Convert HTML to PDF new pose
● Java uses UnixSocket to call Docker API
● Service Mesh - gRPC local joint debugging remote service
● Dynamically rendering HTML with Thymeleaf
● Spring Boot 2 integrated log4j2 log framework
● Java interview key points summary core chapter reference answer
● The framework of Java interview key points summary
● Spring Security: how to protect user password
● Spring Boot RabbitMQ - priority queue
Original link: https://mp.weixin.qq.com/s? Biz = mzu0mdewmjwwna = & mid = 2247486167 & IDX = 2 & Sn = 76dba01d116b7147c9b1dfb7cbf2d8d28d228 & chksm = fb3f132ccc4489a3a3a3a2053148323d 660c440e8af90dcd33580042289958f98b44a258d223badba8 & token = 280305379 & lang = en ABCD CN Rd
This article is based on the platform of blog one article multiple sending OpenWrite Release!