This is the 11th day of my participation in the Challenge. For details, see:More article challenges

Set up a Flag and write something every day and stick to it.

Distributed Shiro permission authentication

Shiro is a lightweight permission authentication framework that has been used in many previous projects for background management system permission authentication without the separation of front and back end. Spring Security is usually used in new projects, and spring provides a well-supported framework. The common annotation for Shiro is @requirespermissions @requiresroles @requiresuser.

Build shiro project

Introduced dependencies on Shiro, reddisHttpSession, this chestnut uses Thymeleaf

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring-boot-web-starter</artifactId>
  <version>1.7.1</version>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-data-redis</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Copy the code

To create Shiro’s configuration class, you must inject UserRealm, configure the path to intercept, and authenticate HashedCredentialsMatcher with a password

@Configuration
public class ShiroConfig {

  @Bean
  public UserRealm userRealm(a) {
    UserRealm userRealm = new UserRealm();
    userRealm.setCredentialsMatcher(this.credentialsMatcher());
    return userRealm;
  }

  @Bean
  public ShiroFilterChainDefinition shiroFilterChainDefinition(a) {
    DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
    chainDefinition.addPathDefinition("/captcha"."anon");
    chainDefinition.addPathDefinition("/logout"."anon");
    chainDefinition.addPathDefinition("/layuiadmin/**"."anon");
    chainDefinition.addPathDefinition("/druid/**"."anon");
    chainDefinition.addPathDefinition("/api/**"."anon");
    chainDefinition.addPathDefinition("/login"."anon");
    chainDefinition.addPathDefinition("/ * *"."authc");
    return chainDefinition;
  }

  @Bean
  public HashedCredentialsMatcher credentialsMatcher(a) {
    HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
    credentialsMatcher.setHashAlgorithmName("SHA-256");
    credentialsMatcher.setStoredCredentialsHexEncoded(false);
    credentialsMatcher.setHashIterations(1024);
    return credentialsMatcher;
  }

  @Bean
  public SessionsSecurityManager securityManager(a) {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(this.userRealm());
    returnsecurityManager; }}Copy the code

The UserRealm class implements two interfaces for user authentication and authorization. It can obtain user account information from the database to authenticate login, and obtain permission information to set permissions and roles.

public class UserRealm extends AuthorizingRealm {

  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    User user = (User)SecurityUtils.getSubject().getPrincipal();
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    Set<String> roles = new HashSet();
    Set<String> permissions = new HashSet();
    if ("admin".equals(user.getUserName())) {
      roles.add("admin");
      permissions.add("op:write");
    } else {
      roles.add("user");
      permissions.add("op:read");
    }

    authorizationInfo.setRoles(roles);
    authorizationInfo.setStringPermissions(permissions);
    return authorizationInfo;
  }

  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    String username = (String)authenticationToken.getPrincipal();
    User user = new User();
    user.setUserName("admin");
    String password = "123456";
    String salt = "salt";
    int hashIterations = 1024;
    String encodedPassword = (new SimpleHash("SHA-256", password, Util.bytes(salt), hashIterations)).toBase64();
    user.setPassword(encodedPassword);
    SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), Util.bytes(salt), this.getName());
    return authenticationInfo;
  }
Copy the code

Write a simple page

Login page login. HTML and index.html

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Shiro Login</title>
</head>
<body>
<h3>Home Login</h3>
<div>port: <span>[[${#httpServletRequest.getServerPort()}]]</span></div>
<div>session: <span>[[${#httpServletRequest.getSession().getId()}]]</span></div>
<div>time: <span>[[${#dates.format(new java.util.Date().getTime(), 'yyyy-MM-dd hh:mm:ss')}]]</span></div>
<form method="post" action="/login">
    <div><label for="userName">UserName:</label><input type="text" name="userName" value="admin" id="userName"></div>
    <div><label for="password">Password:</label><input type="password" name="password" value="123456" id="password"></div>
    <div><input type="submit" value="Submit"></div>
</form>

</body>
</html>
Copy the code
<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Shiro Session</title>
</head>
<body>
<h3>Shiro Session home</h3>
<div>port: <span>[[${#httpServletRequest.getServerPort()}]]</span></div>
<div>session: <span>[[${#httpServletRequest.getSession().getId()}]]</span></div>
<div>time: <span>[[${#dates.format(new java.util.Date().getTime(), 'yyyy-MM-dd hh:mm:ss')}]]</span></div>

</body>
</html>
Copy the code

Logon logic for processing

@RequestMapping(value = "/login",method = {RequestMethod.GET,RequestMethod.POST})
public String doLogin (@RequestParam(required = false) String userName, @RequestParam(required = false) String password,
    @RequestParam(required = false) String easyCaptcha, HttpServletRequest request) {
  Object principal = SecurityUtils.getSubject().getPrincipal();
  if(principal ! =null) {
    return "index";
  }
  if(! StrUtil.isEmpty(userName) && ! StrUtil.isEmpty(password)) {try {
      UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
      SecurityUtils.getSubject().login(token);
      HashMap<String, String> map = new HashMap(16);
      map.put("access_token"."1111111111111111111");
      return "index";
    } catch (IncorrectCredentialsException var7) {
      log.info("Password error {}", var7.getMessage());
    } catch (UnknownAccountException var8) {
      log.info("Account does not exist {}", var8.getMessage());
    } catch (LockedAccountException var9) {
      log.info("Account locked {}", var9.getMessage());
    } catch (ExcessiveAttemptsException var10) {
      log.info("Frequent operation, please try again later {}", var10.getMessage());
    } catch (Exception var11) {
      log.error("Login exception {}", var11.getMessage(), var11); }}return "login";
}
Copy the code

At this point, Shiro’s system is complete. A project based on Shro-spring-boot-web-starter is that simple.

Add annotations to the project startup class, inject the registry, and enable redisHttpSession, which resolves the cookie session ID and stores the data in reIDS to implement distributed sessions.

@EnableRedisHttpSession
@EnableDiscoveryClient
@SpringBootApplication
Copy the code

Add the project to the gateway and access through the gateway. After login, distributed session can be implemented. Note that the session scope must be in the same domain domain to be valid.

For projects that are separated from the front and back ends, you can log in and get a cookie with a sessionId on each cookie request to implement Shiro’s distributed session.

Gitee code: gitee.com/tg_seahorse…