SSO client design
The security authentication of SSO client is encapsulated by merchan-Security module, so that each SSO client application can reference it.
Project management configuration for security authentication
The project management of SSO client security authentication uses the following dependency configuration:
<dependencies>
<dependency>
<groupId>com.demo</groupId>
<artifactId>merchant-client</artifactId><version>${project.version)</version></dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId></dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId></dependency>
</dependencies>
Copy the code
In addition to the Spring Cloud OAuth 2 component, this configuration mainly refers to the merchant-client module to provide the function of invoking the merchant service interface, and the Spring Boot Redis component to provide the function of using cache.
Configuration class of security authentication items
Enabling Spring Security authentication in SSO’s client is primarily done through a configuration class. As shown in the following code, we create a configuration class SecurityConfiguration, it inherits the WebSecurityConfigurerAdapter:
@Configuration @EnableOAuth2Sso @EnableConfigurationProperties (SecuritySettings.class) public class SecurityConfiguration extends webSecurityConfigurerAdapter{ @Autowired private AuthenticationManager authenticationManager; @Autowired private SecuritySettings settings; @Autowired private RoleRestService roleRestService; @Autowired private RedisCache redisCache; @Bean(name = BeanIds.AUTHENT ICATION MANAGER)Goverride public AuthenticationManager authenticationManagerBean() throws Excepti return super.authenticationManagerBean(); ) @override public void configure(HttpSecurity http) throws Exception http.antMatcher("/**") .authorizeRequests() .antMatchers( "/login**").permitAll() .antMatchers(settings.getPermitall()). permitAll() .anyRequest() .authenticated() .and ().csrf().requireCsrfProtectionMatcher (csrfSecurityRequest Matcher()) .csrfTokenRepository (csrfTokenRepository()) .and() .addFilterAfter (csrfHeaderFilter(),CsrfFilter.class).logout() .logoutUrl("/logout").permitAll() LogoutSuccessUrl (Settings. GetLogoutsuccssurl ()) and (). ExceptionHandling () accessDeniedPage (Settings. GetDeniedpage (); } @Bean public CustomFilterSecurityInterceptor customFilter() throws Exception t CustomFilterSecurityInterceptor customFilter = new CustomFilterSecurityInterceptor(); customFilter.setSecurityMetadataSource (securityMetadataSource()); customFilter.setAccessDecisionManager(accessDecisionManager()); customFilter.setAuthenticationManager(authenticationManager); return customFilter; } @Bean public CustomAccessDecisionManager accessDecisionManager() { return new CustomAccessDecisionManager(); } @Bean public CustomSecurityMetadataSource securitywetadataSource(){ return new CustomSecurityMetadataSource(roleRestService,redisCache) ; }}Copy the code
This configuration class implements the following functions:
(1) Use the @enableoAuth2SSO annotation to enable the SSO client function of the application.
(2) Use some custom security configurations by overriding the configure method. SecuritySettings is a custom configuration class that provides configuration parameters for links to exit successfully, links to allow access, and links to deny access.
(3) In the configuration through a custom filter customFilter introduced several other custom design starting with Custom, including security management metadata and permission management verification design.
Permission management validation design
The user permission verification design is mainly composed of two parts, namely, secure resource metadata management and user access to resource permission check.
Create a security resources metadata management class CustomSecurityMetadataSource, implement security resources metadata management design, code as shown below:
public class CustomSecurityMetadataSource implementsFilterInvocationSecurityMetadatasource{ private static final Logger logger = LoggerFactory.getLogger (CustomSecurityMetadataSource.class); public static final String MERCHANT CENTER ROLES ALL="MERCHANT_ CENTER ROLES ALL"; private PathMatcher pathMatcher = new AntPathMatcher(); private RoleRestService roleRestService; private RedisCache rediscache; coverride public Collection<ConfigAttribute> getAllConfigAttributes() return null; public CustomSecurityMetadataSource (RoleRestService roleRestSeRedisCache redisCache) { super(); this.roleRestService = roleRestService; this.redisCache = redisCache; private List<RoleQ0> loadResourceWithRoles()i String roles = roleRestService.findList(); List<RoleQ0> list = new Gson().fromJson (roles, new TypeToken<List<RoleQ0>>() {]}.getType()); if(list ! =null) { redisCache.set (MERCHANT_CENTER_ROLES_ALL + "LIST",list, 180); } return list; } @override public Collection<configAttribute> getAttributes(0bject object) throws IllegalArgumentException { String url = ((FilterInvocation) object).getRequestUrl(); //logger.info(" Request resource: er.info(" request resource :" URL); 0bject objects = rediscache. get (MERCHANT CENTER ROLES ALL + "LIST"); List<RoleQo> roleQoList = null; If (commonUtils.isnull (objects)) {roleQoList = loadResourceWithRoles(); Else {roleQoList=(List<RoleQo>)objects; } Collection<ConfigAttribute> roles = new ArrayList<>(); // List of authorized roles // Check the resources of each role. If they match the requested resources, add them to the role list. If (roleQoList! =null && roleqolist.size () >0){for (RoleQo role0o :roleQoList) {// List of loop roles <ResourceQo> resourceQos = roleQo.getResources(); if(resourceQos ! = null && resourceqos.size () >0) for (ResourceQo ResourceQo :resourceQos){// Loop resource list if(resourceqo.geturl ()! =null && pathMatcher. Match (resourceQo.getur1 ()+"/**", URL)) {ConfigAttribute attribute =new SecurityConfig(roleQo.getName()); roles.add(attribute); Logger. debug(" Add privilege role list === Role resource :{,role name :{}=-=", resourceQo.geturl (), roleqo.getName ()); break; } } } } } return roles; }}Copy the code
In this design, mainly by rewriting FilterInvocationSecurityMetadataSource getAttributes method, from the user to access the URL of the resource, the role of the inspection system permissions list whether there is a match with each other.
If there is,
It is stored in a role list of security metadata. This list will provide the basis for subsequent permission checks
As you can see from this, if we do not specify which role has access to a resource, then all users have access.
Because the metadata management of resources uses the method of dynamic loading, so the user’s permission management can also achieve online update. At the same time, the cache technology is also used to improve the access performance of metadata.
With metadata management for secure resources, we can perform real-time permission checks on user behavior.
Create a permission check implementation class CustomAccessDecisionManager, if a user has access to resources on the real-time permission check. This class implements the AccessDecisionManager as follows:
public class CustomAccessDecisionManager implements AccessDecisionManager { private static final Logger logger = LoggerFactory.getLogger (CustomAccessDecisionManager.class); @Override public void decide (Authentication authentication,0bject object, Collection<ConfigAttribute>configAttributes) throws AccessDeniedException,InsufficientAuthenticationExceptionif(configAttributes == nul1){ return; } / / from CustomSecurityMetadataSource (getAttributes) get request in the resources needed to set the role of the Iterator < ConfigAttribute > Iterator = configAttributes.iterator(); while (iterator.hasNext()){ ConfigAttribute configAttribute= iterator.next(); / / have access to resources character String needRole = configAttribute. The getAttribute (); Logger.debug (" Role with permissions :" +needRole); For (GrantedAuthority ga: 1 if authentication. GetAuthorities ()) (needRole. Equals (ga getAuthority ())) {return; Throw new AccessDeniedException(" No access!"); ); }}Copy the code
In this design, by rewriting the access decision method of AccessDecisionManager, the role in the security management metadata is compared with the role of the user. If the role of the user matches the role of the metadata, the user has access permission, otherwise the user has no access permission.
Sso is connected to the client application
With SSO client security management encapsulation, a Web application that needs SSO access can use SSO functions by adding a reference to the SSO client security management component in the project management configuration of the application.
The following uses merchant- Web as an example. Other Web UI applications can use this method to access SSO. In the merchant management background, the client applications that need to access SSO include inventory management, order management, and logistics management, which can be decided according to actual needs.
First, reference the encapsulation component of SSO client security management in the project configuration management code as follows:
<! <dependency> <groupId>com.demo</groupId> <artifactId>merchant-security</artifactId><version>${project.version)</version> </dependency>Copy the code
Because it is referenced within the same project, the project version number is used. If it is referenced by another application, change the above version to 2.1-snapshot.
Second, use the following Settings in the application configuration file application.yml:
Spring :redis: host: 127.0.0.1port: 6379 Security: oauth2: client: client-id: ssoclient client-secret: ssosecret access-token-uri: http://localhost:8000/oauth/token user-authorization-uri: http://localhost:8000/oauth/authorizeresource: token-info-uri:http://localhost:8000/oauth/check token securityconfig: Logoutsuccssurl: / tosignoutpermitall: - / test/big u - / physical / * deniedpage: / deny ssohome: http://localhost:8000/Copy the code
Redis uses a local server, and the security.oauth2 configuration item is some of the configuration parameters used by the Spring Cloud OAuth2 component. We use these parameters to set clientLD and clientSecret, the client ID and key set in the SSO server design configuration class. The accessTokenUri and userAuthorizationUri are used to specify the endpoint that obtains the token and authenticates, respectively.
The Settings below the SecurityConfig configuration item are set by several custom configuration parameters provided by the configuration class SecuritySettings. Ssohome provides a link to the SSO home page for client applications that access SSO.
In addition to the above configuration, data editing and management needs to be adjusted for SSO enabled Web applications to ensure that data creation and editing can be submitted properly. Additionally, SSO enabled applications can automatically assign menus based on user permissions.
Settings related to cross-site requests
After using Spring Security, you must add Settings for cross-site request forgery defense to the page so that forms can be submitted when data is created or edited, or requests for form submission will be denied access.
First add the following code to the header of the page template loyout.html:
<meta name=" csrf" th:content="${csrf.token}"/> <meta name=" csrf header" th:content="$ { csrf,headerName }"/> $(function (){var token = $("meta [name='_csrf']").attr("content"); $(function (){var token = $("meta [name='_csrf']"). var header = $ ("meta[name='csrf header']").attr("content"); $ (document).ajaxSend(function (e, xhr, options) { xhr.setRequestHeader (header, token); }); });Copy the code
The purpose of this is to enable the background to verify the validity of the page form submission, so as to protect the data submission.
Automatically assign menus based on user permissions
Through the associated object of the login user, we can get a menu list of the user, so that the system menu of the user can be automatically assigned according to the user permissions.
In the application’s home controller, UserController, use the following implementation:
@Controller
@RequestMapping(“/user”)
public class UserController extends BaseController{
CRequestMapping(“/index”)
Public String index (ModelMap model, Principal user, HttpServletRequestrequest) throws the Exception {
List menus = super.getModels(user.getName(), request); model.addAttribute(“menus”, menus);
model .addAttribute(“user”,user);
model .addAttribute(“ssohome”, ssohome); return “user/index”;
}
}
Where, getModels is the concrete implementation of getting the user menu, as shown below:
public abstract class BaseController { private PathMatcher pathMatcher = new AntpathMatcher(); @Autowired private UserRestService userService; @value("${spring. application.name} ")private string serviceName; Public List < ModelQo > getModels (String userName, HttpServletRequestrequest) {/ / retrieve the user object String according to the logged in user json = userService.findByName (userName) ; UserQo user = new Gson().fromJson (json,UserQo.class); List<ModelQo> modelList = new ArrayEist<>(); List<Long>modelIds = new ArrayList<>(); for(RoleQo role : user.getRoles()){ for(ResourceQo resource : role.getResources()){ String link = resource.getModel().getKind ().getLink (); // Category top // menu link // get module list, go to if(! modelIds.contains (resource.getModel().getId()) & & pathMatcher.match(serviceName, link)){ modelList.add(resource. getModel ()); modelIds.add(resource.getModel().get Id()); } } } return modelList; }}Copy the code
Firstly, the list of resources associated with the user is found from the user’s role. Then from each resource to find the module object matching the current application name, and finally after reorganizing the module list, is an application menu system.
By using this list of modules, you can use the following design on your application’s navigation page nav.html, looping out the menu:
<ul>
<li th:each="model:$ {menus}"><a th:classappend="${page ==
'$ {model.host}' ? 'currentPageNav': '')"th:href="e{${model.host}}"th: text="$ {model.name } "></a></li>
</ul>
Copy the code
Through the above design, you can realize the function of automatically assigning menus according to user permissions. With the above development done, you are ready to test.
First verify that the registry is started, then start the merchant-Rest API application, merchant-SSO application, and merchant-Web application, respectively.
After all applications have started successfully, open the link to the merchant system Web application through your browser :http://localhost:8081
After opening the link, enter the user name and password generated in the previous unit test on the login page.
After successful login, you can open the home page of merchant- Web application.
The merchant system has only one user management function, so its home page is shown in Figure 10-8. Here, the merchant administrator can perform user management operations.
summary
This chapter can be used in the messaging platform of micro-service applications developed separately through the unified permission management, which can be used by business users in any other connection in a distributed environment
Applications. In a more perfect way, the business management background design combines the micro-service applications developed separately into a whole with rich functions, which fully reflects the powerful advantages of micro-service architecture design.
Among them, the business authority system design, based on access to resources to establish a three-level menu system, and through the relationship between roles and resources, user authority and menu into an organic whole.
The role of the merchant and its menu management configuration must be managed by the platform operator. In the next part of the platform management background development, will achieve the management of business authority configuration function.
Three things to watch ❤️
If you find this article helpful, I’d like to invite you to do three small favors for me:
-
Like, forward, have your “like and comment”, is the motivation of my creation.
-
Follow the public account “Java rotten pigskin” and share original knowledge from time to time.
-
Also look forward to the follow-up article ing🚀
-
[666] Scan the code to obtain the learning materials package
Article is not original, and all of us feel while reading the article good remember thumb up forward + attention oh ~ this article source: www.toutiao.com/i6904620391…