Shiro integrates the actual combat of SpringBoot project

Posted by mattmate on Sat, 22 Jan 2022 07:31:13 +0100

6. Integrate SpringBoot project practice

6.0 integration ideas

6.1 creating a springboot project

6.2 introducing shiro dependency

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

6.3 configuring shiro environment

0. Create configuration class

1. Configure shiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager){
  //Create shiro's filter
  ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
  //Injection Security Manager
  shiroFilterFactoryBean.setSecurityManager(securityManager);
 	
  return shiroFilterFactoryBean;
}
2. Configure WebSecurityManager
@Bean
public DefaultWebSecurityManager getSecurityManager(Realm realm){
  DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
  defaultWebSecurityManager.setRealm(realm);
  return defaultWebSecurityManager;
}
3. Create a custom realm

public class CustomerRealm extends AuthorizingRealm {
    //Processing authorization
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
		//Process authentication
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws 
      																																		AuthenticationException {
        return null;
    }
}
4. Configure custom realm
//Create custom realm
@Bean
public Realm getRealm(){
  return new CustomerRealm();
}
5. Write the controller and jump to index html
@Controller
public class IndexController {
    @RequestMapping("index")
    public String index(){
        System.out.println("Jump to home page");
        return "index";
    }
}


6. Start the springboot application to access the index

  • be careful:
    • By default, after the shiro environment is configured, there is no permission control for any resources in the project in the default environment. All resources in the project can be accessed through paths
7. Add permission control
  • Modify ShiroFilterFactoryBean configuration

    //Injection Security Manager
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    Map<String,String> map =  new LinkedHashMap<>();
    map.put("/**","authc");
    //Configure authentication and authorization rules
    shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
    

    [the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-932Hxa4K-1625454917389)(Shiro practical tutorial. assets/image-20200523102303320.png)]

    • /**authc represents the alias of a filter in shiro. See the shiro filter list in the document for details
8. Restart project access view

6.4 common filters

  • Note: shiro provides and multiple default filters. We can use these filters to configure the permission to control the specified url:
Configuration abbreviationCorresponding filterfunction
anonAnonymousFilterSpecifies that the url can be accessed anonymously
authcFormAuthenticationFilterThe specified url requires form login. By default, the user name, password,rememberMe and other parameters will be obtained from the request and try to log in. If the login fails, it will jump to the path configured by loginUrl. We can also use this filter as the default login logic, but we usually write the login logic in the controller ourselves. If we write it ourselves, the error returned information can be customized.
authcBasicBasicHttpAuthenticationFilterThe specified url requires basic login
logoutLogoutFilterLog out of the filter and configure the specified url to realize the exit function, which is very convenient
noSessionCreationNoSessionCreationFilterDisable session creation
permsPermissionsAuthorizationFilterYou need to specify permissions to access
portPortFilterYou need to specify a port to access
restHttpMethodPermissionFilterConvert the http request method into the corresponding verb to construct a permission string. This doesn't make much sense. I'm interested in reading the comments of the source code
rolesRolesAuthorizationFilterYou need to specify a role to access
sslSslFilterAn https request is required to access
userUserFilterUsers who are logged in or remember me are required to access

6.5 certification realization

1. In login Developing authentication interface in JSP

<form action="${pageContext.request.contextPath}/user/login" method="post">
  user name:<input type="text" name="username" > <br/>
  password  : <input type="text" name="password"> <br>
  <input type="submit" value="Sign in">
</form>
2. Develop controller
@Controller
@RequestMapping("user")
public class UserController {
  /**
    * Used to handle identity authentication
    * @param username
    * @param password
    * @return
    */
  @RequestMapping("login")
  public String login(String username,String password){
    //Get principal object
    Subject subject = SecurityUtils.getSubject();
    try {
      subject.login(new UsernamePasswordToken(username,password));
      return  "redirect:/index.jsp";
    } catch (UnknownAccountException e) {
      e.printStackTrace();
      System.out.println("User name error!");
    }catch (IncorrectCredentialsException e){
      e.printStackTrace();
      System.out.println("Password error!");
    }
    return "redirect:/login.jsp";
  }
}
  • Use subject in the authentication process Login for authentication
3. Static data returned in the development realm (database not connected)
@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("==========================");
        String principal = (String) token.getPrincipal();
        if("xiaochen".equals(principal)){
            return new SimpleAuthenticationInfo(principal,"123",this.getName());
        }
        return null;
    }
}
4. Start the project to authenticate the static data defined in realm



  • The authentication function is realized without md5 and random salt authentication

6.6 withdrawal from certification

1. Exit the connection from the development page

2. Develop controller
@Controller
@RequestMapping("user")
public class UserController {
  /**
    * Log out
    *
    */
  @RequestMapping("logout")
  public String logout(){
    Subject subject = SecurityUtils.getSubject();
    subject.logout();//Exit user
    return "redirect:/login.jsp";
  }
}
3. Modify exit connection access exit path

4. After exiting, access restricted resources and return to the authentication interface immediately

6.7 MD5 and Salt authentication implementation

1. Development database registration

0. Develop registration interface
<h1>User registration</h1>
<form action="${pageContext.request.contextPath}/user/register" method="post">
  user name:<input type="text" name="username" > <br/>
  password  : <input type="text" name="password"> <br>
  <input type="submit" value="Register now">
</form>

1. Create data table structure
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `username` varchar(40) DEFAULT NULL,
  `password` varchar(40) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

SET FOREIGN_KEY_CHECKS = 1;

2. Project introduction dependency
<!--mybatis Correlation dependency-->
<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.1.2</version>
</dependency>

<!--mysql-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.38</version>
</dependency>


<!--druid-->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.19</version>
</dependency>
3. Configure application Properties configuration file
server.port=8888
server.servlet.context-path=/shiro
spring.application.name=shiro

spring.mvc.view.suffix=/
spring.mvc.view.prefix=.jsp

#data source
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=123456

mybatis.type-aliases-package=com.baizhi.springboot_jsp_shiro.entity
mybatis.mapper-locations=classpath:com/baizhi/mapper/*.xml

#redis
spring.redis.database=0
spring.redis.host=192.168.159.22
spring.redis.port=6379




#logging.level.root=debug
logging.level.com.baizhi.springboot_jsp_shiro.dao=debug




4. Create entity
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String  id;
    private String username;
    private String password;
    private String salt;
}
5. Create DAO interface
@Mapper
public interface UserDAO {
    void save(User user);
}
6. Develop mapper configuration file
<insert id="save" parameterType="User" useGeneratedKeys="true" keyProperty="id">
  insert into t_user values(#{id},#{username},#{password},#{salt})
</insert>
7. Develop service interface
public interface UserService {
    //Registered user method
    void register(User user);
}
8. Create salt tool class
public class SaltUtils {
    /**
     * Static method for generating salt
     * @param n
     * @return
     */
    public static String getSalt(int n){
        char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890!@#$%^&*()".toCharArray();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            char aChar = chars[new Random().nextInt(chars.length)];
            sb.append(aChar);
        }
        return sb.toString();
    }
}
9. Develop service implementation class
@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDAO userDAO;

    @Override
    public void register(User user) {
        //Processing business calls dao
        //1. Generate random salt
        String salt = SaltUtils.getSalt(8);
        //2. Save the random salt to the data
        user.setSalt(salt);
        //3. md5 + salt + hash for plaintext password
        Md5Hash md5Hash = new Md5Hash(user.getPassword(),salt,1024);
        user.setPassword(md5Hash.toHex());
        userDAO.save(user);
    }
}
10. Develop Controller
@Controller
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * User registration
     */
    @RequestMapping("register")
    public String register(User user) {
        try {
            userService.register(user);
            return "redirect:/login.jsp";
        }catch (Exception e){
            e.printStackTrace();
            return "redirect:/register.jsp";
        }
    }
}
11. Start the project for registration

2. Develop database certification

0. Develop DAO
@Mapper
public interface UserDAO {

    void save(User user);
		//Method of authentication according to identity information
    User findByUserName(String username);
}
1. Develop mapper configuration file
<select id="findByUserName" parameterType="String" resultType="User">
  select id,username,password,salt from t_user
  where username = #{username}
</select>
2. Develop Service interface
public interface UserService {
    //Registered user method
    void register(User user);
    //Method of querying business according to user name
    User findByUserName(String username);
}
3. Develop Service implementation class
@Service("userService")
@Transactional
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDAO userDAO;
    @Override
    public User findByUserName(String username) {
        return userDAO.findByUserName(username);
    }
}
4. Develop the tool class to obtain the bean object in the factory
@Component
public class ApplicationContextUtils implements ApplicationContextAware {

    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }


    //Get the specified bean object in the factory according to the bean name
    public static Object getBean(String beanName){
        return context.getBean(beanName);
    }
}
5. Modify custom realm
 @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("==========================");

        //According to identity information
        String principal = (String) token.getPrincipal();
        //Get the service object in the factory
        UserService userService = (UserService) ApplicationContextUtils.getBean("userService");
				//Query according to identity information
        User user = userService.findByUserName(principal);

        if(!ObjectUtils.isEmpty(user)){
            //Return database information
            return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), 
                                               ByteSource.Util.bytes(user.getSalt()),this.getName());
        }
        return null;
    }
6. Modify the voucher matcher and hash hash used by realm in ShiroConfig
@Bean![Insert picture description here](https://img-blog.csdnimg.cn/20210705115139870.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzUzNzI0,size_16,color_FFFFFF,t_70#pic_center)

public Realm getRealm(){
  CustomerRealm customerRealm = new CustomerRealm();
  //Set hashed credential matcher
  HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
  //Set md5 encryption
  credentialsMatcher.setHashAlgorithmName("md5");
  //Sets the number of hashes
  credentialsMatcher.setHashIterations(1024);
  customerRealm.setCredentialsMatcher(credentialsMatcher);
  return customerRealm;
}

6.8 authorization realization

0. Page resource authorization
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

<shiro:hasAnyRoles name="user,admin">
        <li><a href="">user management </a>
            <ul>
                <shiro:hasPermission name="user:add:*">
                <li><a href="">add to</a></li>
                </shiro:hasPermission>
                <shiro:hasPermission name="user:delete:*">
                    <li><a href="">delete</a></li>
                </shiro:hasPermission>
                <shiro:hasPermission name="user:update:*">
                    <li><a href="">modify</a></li>
                </shiro:hasPermission>
                <shiro:hasPermission name="user:find:*">
                    <li><a href="">query</a></li>
                </shiro:hasPermission>
            </ul>
        </li>
        </shiro:hasAnyRoles>
        <shiro:hasRole name="admin">
            <li><a href="">Commodity management</a></li>
            <li><a href="">Order management</a></li>
            <li><a href="">physical distribution management</a></li>
        </shiro:hasRole>
1. Code authorization
@RequestMapping("save")
public String save(){
  System.out.println("Entry method");
  //Get principal object
  Subject subject = SecurityUtils.getSubject();
  //Code mode
  if (subject.hasRole("admin")) {
    System.out.println("Save order!");
  }else{
    System.out.println("No access!");
  }
  //Based on permission string
  //....
  return "redirect:/index.jsp";
}

2. Method call authorization
  • @RequiresRoles are used to authorize based on roles
  • @RequiresPermissions is used to authorize based on permissions
@RequiresRoles(value={"admin","user"})//Used to judge that the role also has admin user
@RequiresPermissions("user:update:01") //String used to determine permission
@RequestMapping("save")
public String save(){
  System.out.println("Entry method");
  return "redirect:/index.jsp";
}

3. Authorization data persistence

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_pers
-- ----------------------------
DROP TABLE IF EXISTS `t_pers`;
CREATE TABLE `t_pers` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `name` varchar(80) DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_role
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `name` varchar(60) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_role_perms
-- ----------------------------
DROP TABLE IF EXISTS `t_role_perms`;
CREATE TABLE `t_role_perms` (
  `id` int(6) NOT NULL,
  `roleid` int(6) DEFAULT NULL,
  `permsid` int(6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `username` varchar(40) DEFAULT NULL,
  `password` varchar(40) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_user_role
-- ----------------------------
DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role` (
  `id` int(6) NOT NULL,
  `userid` int(6) DEFAULT NULL,
  `roleid` int(6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

SET FOREIGN_KEY_CHECKS = 1;

4. Create dao method
 //Query all roles by user name
User findRolesByUserName(String username);
//Query permission collection according to role id
List<Perms> findPermsByRoleId(String id);
5.mapper implementation
<resultMap id="userMap" type="User">
  <id column="uid" property="id"/>
  <result column="username" property="username"/>
  <!--Role information-->
  <collection property="roles" javaType="list" ofType="Role">
    <id column="id" property="id"/>
    <result column="rname" property="name"/>
  </collection>
</resultMap>

<select id="findRolesByUserName" parameterType="String" resultMap="userMap">
  SELECT u.id uid,u.username,r.id,r.NAME rname
  FROM t_user u
  LEFT JOIN t_user_role ur
  ON u.id=ur.userid
  LEFT JOIN t_role r
  ON ur.roleid=r.id
  WHERE u.username=#{username}
</select>

<select id="findPermsByRoleId" parameterType="String" resultType="Perms">
  SELECT p.id,p.NAME,p.url,r.NAME
  FROM t_role r
  LEFT JOIN t_role_perms rp
  ON r.id=rp.roleid
  LEFT JOIN t_perms p ON rp.permsid=p.id
  WHERE r.id=#{id}
</select>
6.Service interface
//Query all roles by user name
User findRolesByUserName(String username);
//Query permission collection according to role id
List<Perms> findPermsByRoleId(String id);
7.Service implementation
@Override
public List<Perms> findPermsByRoleId(String id) {
  return userDAO.findPermsByRoleId(id);
}

@Override
public User findRolesByUserName(String username) {
  return userDAO.findRolesByUserName(username);
}
8. Modify custom realm
public class CustomerRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //Get identity information
        String primaryPrincipal = (String) principals.getPrimaryPrincipal();
        System.out.println("Invoke authorization validation: "+primaryPrincipal);
        //Obtain role and permission information according to master identity information
        UserService userService = (UserService) ApplicationContextUtils.getBean("userService");
        User user = userService.findRolesByUserName(primaryPrincipal);
        //Authorization role information
        if(!CollectionUtils.isEmpty(user.getRoles())){
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            user.getRoles().forEach(role->{
                simpleAuthorizationInfo.addRole(role.getName());
                //permissions information
                List<Perms> perms = userService.findPermsByRoleId(role.getId());
                if(!CollectionUtils.isEmpty(perms)){
                    perms.forEach(perm->{
                        simpleAuthorizationInfo.addStringPermission(perm.getName());
                    });
                }
            });
            return simpleAuthorizationInfo;
        }
        return null;
    }
}

9. Start up test

6.9 using CacheManager

1.Cache function

  • Cache: a piece of data in computer memory
  • Function: it is used to reduce the access pressure of DB, so as to improve the query efficiency of the system
  • technological process:

2. Use the default EhCache in shiro to implement caching

1. Introduce dependency
<!--introduce shiro and ehcache-->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-ehcache</artifactId>
  <version>1.5.3</version>
</dependency>
2. Enable cache
//3. Create a custom realm
    @Bean
    public Realm getRealm(){
        CustomerRealm customerRealm = new CustomerRealm();
        //Modify voucher verification matcher
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //Set the encryption algorithm to md5
        credentialsMatcher.setHashAlgorithmName("MD5");
        //Sets the number of hashes
        credentialsMatcher.setHashIterations(1024);
        customerRealm.setCredentialsMatcher(credentialsMatcher);

        //Open cache manager
        customerRealm.setCachingEnabled(true);
        customerRealm.setAuthorizationCachingEnabled(true);
        customerRealm.setAuthorizationCachingEnabled(true);
        customerRealm.setCacheManager(new EhCacheManager());
        return customerRealm;
    }

3. Start the refresh page to test
  • Note: if there is no sql presentation on the console, the cache has been enabled

3. Redis is used as cache implementation in Shiro

1. Introduce redis dependency
<!--redis integration springboot-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. Configure redis connection

Here I use redis on the virtual machine

#redis
spring.redis.database=0
spring.redis.host=192.168.159.22
spring.redis.port=6379
3. Start redis service
➜  bin ls
dump.rdb        redis-check-aof redis-cli       redis-server    redis.conf
redis-benchmark redis-check-rdb redis-sentinel  redis-trib.rb
➜  bin ./redis-server redis.conf
4. Develop RedisCacheManager
public class RedisCacheManager implements CacheManager {
    @Override
    public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException {
        System.out.println("Cache name: "+cacheName);
        return new RedisCache<K,V>(cacheName);
    }
}
5. RedisCache implementation
public class RedisCache<K,V> implements Cache<K,V> {

    private String cacheName;

    public RedisCache() {
    }

    public RedisCache(String cacheName) {
        this.cacheName = cacheName;
    }

    @Override
    public V get(K k) throws CacheException {
        System.out.println("Get cache:"+ k);
        return (V) getRedisTemplate().opsForHash().get(this.cacheName,k.toString());
    }

    @Override
    public V put(K k, V v) throws CacheException {
        System.out.println("Set cache key: "+k+" value:"+v);
        getRedisTemplate().opsForHash().put(this.cacheName,k.toString(),v);
        return null;
    }

    @Override
    public V remove(K k) throws CacheException {
        return (V) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString());
    }

    @Override
    public v remove(k k) throws CacheException {
        return (v) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString());
    }

    @Override
    public void clear() throws CacheException {
        getRedisTemplate().delete(this.cacheName);
    }

    @Override
    public int size() {
        return getRedisTemplate().opsForHash().size(this.cacheName).intValue();
    }

    @Override
    public Set<k> keys() {
        return getRedisTemplate().opsForHash().keys(this.cacheName);
    }

    @Override
    public Collection<v> values() {
        return getRedisTemplate().opsForHash().values(this.cacheName);
    }

    private RedisTemplate getRedisTemplate(){
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }


    //Encapsulate and obtain redisTemplate
    private RedisTemplate getRedisTemplate(){
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}
6. Start the project test and report an error


  • Error explanation: because the simpleByteSource implementation provided in shiro does not realize serialization, all error messages appear during authentication

  • Solution: automatic salt serialization is required

    • Custom salt implementation serialization

      //Custom salt serialization interface
      public class MyByteSource extends SimpleByteSource implements Serializable {
          public MyByteSource(String string) {
              super(string);
          }
      }
      
    • Using custom salt in realm

       @Override
      protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("==========================");
        //According to identity information
        String principal = (String) token.getPrincipal();
        //Get the service object in the factory
        UserService userService = (UserService) ApplicationContextUtils.getBean("userService");
        User user = userService.findByUserName(principal);
        if(!ObjectUtils.isEmpty(user)){
          return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), 
                                            new MyByteSource(user.getSalt()),this.getName());
        }
        return null;
      }
      

7. Start the test again and find that it can be successfully put into the redis cache

4. Add verification code

0. Add verification code to the development page
  • Development controller

    @RequestMapping("getImage")
    public void getImage(HttpSession session, HttpServletResponse response) throws IOException {
      //Generate verification code
      String code = VerifyCodeUtils.generateVerifyCode(4);
      //Put the verification code into the session
      session.setAttribute("code",code);
      //Verification code stored in picture
      ServletOutputStream os = response.getOutputStream();
      response.setContentType("image/png");
      VerifyCodeUtils.outputImage(220,60,os,code);
    }
    
  • Release verification code

  • Development page

  • Modify the certification process

    @RequestMapping("login")
        public String login(String username, String password,String code,HttpSession session) {
            //Comparison verification code
            String codes = (String) session.getAttribute("code");
            try {
                if (codes.equalsIgnoreCase(code)){
                    //Get principal object
                    Subject subject = SecurityUtils.getSubject();
                        subject.login(new UsernamePasswordToken(username, password));
                        return "redirect:/index.jsp";
                }else{
                    throw new RuntimeException("Verification code error!");
                }
            } catch (UnknownAccountException e) {
                e.printStackTrace();
                System.out.println("User name error!");
            } catch (IncorrectCredentialsException e) {
                e.printStackTrace();
                System.out.println("Password error!");
            }catch (Exception e){
                e.printStackTrace();
                System.out.println(e.getMessage());
            }
            return "redirect:/login.jsp";
        }
    
  • Modify the problem that salt cannot be serialized

    //Custom salt serialization interface
    public class MyByteSource implements ByteSource,Serializable {
    
        private  byte[] bytes;
        private String cachedHex;
        private String cachedBase64;
    
        //Adding parameterless construction method to realize serialization and deserialization
        public MyByteSource(){
    
        }
    
        public MyByteSource(byte[] bytes) {
            this.bytes = bytes;
        }
    
        public MyByteSource(char[] chars) {
            this.bytes = CodecSupport.toBytes(chars);
        }
    
        public MyByteSource(String string) {
            this.bytes = CodecSupport.toBytes(string);
        }
    
        public MyByteSource(ByteSource source) {
            this.bytes = source.getBytes();
        }
    
        public MyByteSource(File file) {
            this.bytes = (new MyByteSource.BytesHelper()).getBytes(file);
        }
    
        public MyByteSource(InputStream stream) {
            this.bytes = (new MyByteSource.BytesHelper()).getBytes(stream);
        }
    
        public static boolean isCompatible(Object o) {
            return o instanceof byte[] || o instanceof char[] || o instanceof String || o instanceof ByteSource || o instanceof File || o instanceof InputStream;
        }
    
        public byte[] getBytes() {
            return this.bytes;
        }
    
        public boolean isEmpty() {
            return this.bytes == null || this.bytes.length == 0;
        }
    
        public String toHex() {
            if (this.cachedHex == null) {
                this.cachedHex = Hex.encodeToString(this.getBytes());
            }
    
            return this.cachedHex;
        }
    
        public String toBase64() {
            if (this.cachedBase64 == null) {
                this.cachedBase64 = Base64.encodeToString(this.getBytes());
            }
    
            return this.cachedBase64;
        }
    
        public String toString() {
            return this.toBase64();
        }
    
        public int hashCode() {
            return this.bytes != null && this.bytes.length != 0 ? Arrays.hashCode(this.bytes) : 0;
        }
    
        public boolean equals(Object o) {
            if (o == this) {
                return true;
            } else if (o instanceof ByteSource) {
                ByteSource bs = (ByteSource)o;
                return Arrays.equals(this.getBytes(), bs.getBytes());
            } else {
                return false;
            }
        }
    
        private static final class BytesHelper extends CodecSupport {
            private BytesHelper() {
            }
    
            public byte[] getBytes(File file) {
                return this.toBytes(file);
            }
    
            public byte[] getBytes(InputStream stream) {
                return this.toBytes(stream);
            }
        }
    }
    

Topics: Mybatis Shiro Spring Boot