springboot + shiro + cas login + autorización + sso inicio de sesión único (2)

springboot + shiro + cas inicio de sesión + autorización + sso inicio de sesión único (1)

En este artículo, usaremos springboot + redis + shiro para desarrollar roles + permisos.

1. Crear un nuevo proyecto maven + springboot, la estructura de mi proyecto es:

sso_login es un proyecto maven. Hay dos módulos en él, que son el proyecto springboot de member y order. El miembro está relacionado principalmente con el inicio de sesión. El pedido es principalmente para probar desde el punto único del sistema miembro al sistema de pedidos.

 

2. estructura de ingeniería miembro

3. Archivo Pom de sso_login:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.gane.maple</groupId>
    <artifactId>sso_login</artifactId>
    <packaging>pom</packaging>
    <version>1.0</version>

    <modules>
        <module>member</module>
        <module>order</module>
    </modules>

    <properties>
        <spring.boot.version>2.2.6.RELEASE</spring.boot.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


</project>

4. El archivo pom del proyecto miembro:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.gane.maple</groupId>
        <artifactId>sso_login</artifactId>
        <version>1.0</version>
    </parent>

    <groupId>com.gane.maple.member</groupId>
    <artifactId>member</artifactId>
    <version>1.0.0</version>
    <name>member</name>
    <description>member system</description>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

        <!-- redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>3.1.0</version>
            <exclusions>
                <exclusion>
                    <groupId>com.puppycrawl.tools</groupId>
                    <artifactId>checkstyle</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- apache -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.3.2</version>
        </dependency>

        <!-- thymeleaf -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!-- 热启动 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

5. archivo Application.properties

# redis setting
jedis.pool.host=xxxxx
jedis.pool.port=6379
jedis.pool.timeout=300000
jedis.pool.password=xxxxx
jedis.pool.database=0
jedis.pool.config.maxTotal=100
jedis.pool.config.maxIdle=10
jedis.pool.config.maxWaitMillis=1000

#thymeleaf
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.cache=false

6. Las páginas debajo de las plantillas son las mismas excepto login.html, pero la descripción es diferente.

index.html

<!DOCTYPE html>
<!--解决th报错 -->
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>thymeleaf</title>
</head>
<body>
<h1> This is index page!</h1>
<h1 th:text="${userName}"></h1>
</body>
</html>

user.html

<!DOCTYPE html>
<!--解决th报错 -->
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>thymeleaf</title>
</head>
<body>
<h1> This is user page!</h1>
<h1 th:text="${userName}"></h1>
</body>
</html>

login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <title>登录</title>
    <link rel="stylesheet" type="text/css" href="/css/common.css" />
</head>
<body>
<form th:action="@{/login}" method="post">
    <div>
        <!--/*@thymesVar id="error" type=""*/-->
        <span id="basic-addon0">&nbsp;</span>
        <span style="font-size: 12px;color: red" th:text="${error}" aria-describedby="basic-addon0"></span>
        <br />
    </div>
    <div>
        <span id="basic-addon1">@</span>
        <input id="user_name" name="username" type="text" placeholder="用户名" aria-describedby="basic-addon1" />

    </div>
    <br />
    <div>
        <span id="basic-addon2">@</span>
        <input id="password" name="password" type="password" placeholder="密码" aria-describedby="basic-addon2" />
    </div>
    <br />
    <button type="submit" style="width:190px;">登 录</button>

</form>
</body>
</html>

 

7. Clase de usuario

package com.gane.maple.member.model;

import lombok.Data;

import java.io.Serializable;

/**
 * @Description TODO
 * @Date 2020/4/9 20:09
 * @Created by 王弘博
 */
@Data
public class User implements Serializable {

    private String username;
    private String password;
}

8 、 ShiroController

package com.gane.maple.member.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

/**
 * @Description TODO
 * @Date 2020/4/9 18:49
 * @Created by 王弘博
 */
@Controller
public class ShiroController {

    private static final Logger logger = LoggerFactory.getLogger(ShiroController.class);

    @GetMapping("/index")
    public String index(Model model) {
        model.addAttribute("userName", "maple");
        return "/index";

    }

    @GetMapping("/user")
    public String user(Model model) {
        model.addAttribute("userName", "maple");
        return "/user";

    }

    @RequiresPermissions("admin:manage")
    @GetMapping("/manage")
    public String manage(Model model) {
        model.addAttribute("userName", "maple");
        return "/user_manage";
    }

    @RequiresPermissions("admin:query")
    @GetMapping("/query")
    public String query(Model model) {
        model.addAttribute("userName", "maple");
        return "/user_query";
    }

    @GetMapping("/login")
    public String login(Model model) {
        model.addAttribute("userName", "maple");
        return "/login";
    }

    @PostMapping("/login")
    public String login(String username, String password) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password, true);
        try {
            logger.info("对用户[" + username + "]进行登录验证..验证开始");
            subject.login(token);
            logger.info("对用户[" + username + "]进行登录验证..验证通过");
        } catch (UnknownAccountException uae) {
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,未知账户");
        } catch (IncorrectCredentialsException ice) {
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,错误的凭证");
        } catch (LockedAccountException lae) {
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,账户已锁定");
        } catch (ExcessiveAttemptsException eae) {
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,错误次数过多");
        } catch (AuthenticationException ae) {
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,堆栈轨迹如下");
            ae.printStackTrace();
        }
        if (subject.isAuthenticated()) {
            logger.info("用户[" + username + "]登录认证通过(这里可以进行一些认证通过后的一些系统参数初始化操作)");
            return "redirect:/user";
        }
        token.clear();
        return "redirect:/login";

    }

    @GetMapping("/logout")
    public String logout() {
        Subject subject = SecurityUtils.getSubject();
        if (subject.isAuthenticated()) {
            subject.logout();
        }
        return "redirect:/login";
    }

    @GetMapping("/403")
    public String unauthorizedRole() {
        logger.info("------没有权限-------");
        return "403";
    }
}

9 、 RedisConfiguration

package com.gane.maple.member.config.redis;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import javax.annotation.Resource;

/**
 * @Description redis配置
 * @Date 2019/7/11 13:09
 * @Created by 王弘博
 */
@Configuration
public class RedisConfiguration {

    @Bean(name = "jedis.pool")
    @Resource
    public JedisPool jedisPool(@Qualifier("jedis.pool.config") JedisPoolConfig config,
                               @Value("${jedis.pool.host}") String host,
                               @Value("${jedis.pool.port}") int port,
                               @Value("${jedis.pool.timeout}") int timeout,
                               @Value("${jedis.pool.password}") String password,
                               @Value("${jedis.pool.database}") int database) {
        return new JedisPool(config, host, port, timeout, password, database);
    }

    @Bean(name = "jedis.pool.config")
    public JedisPoolConfig jedisPoolConfig(@Value("${jedis.pool.config.maxTotal}") int maxTotal,
                                           @Value("${jedis.pool.config.maxIdle}") int maxIdle,
                                           @Value("${jedis.pool.config.maxWaitMillis}") int maxWaitMillis) {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(maxTotal);
        config.setMaxIdle(maxIdle);
        config.setMaxWaitMillis(maxWaitMillis);
        return config;
    }
}

10. RedisClient (no utilizado)

package com.gane.maple.member.config.redis;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description redis常用方法
 * @Date 2019/7/11 13:09
 * @Created by 王弘博
 */
@Component
public class RedisClient {

    private Logger logger = LoggerFactory.getLogger(RedisClient.class);

    @Resource
    private JedisPool jedisPool;

    public Long del(String key) {
        Long value = null;
        Jedis jedis = null;
        try {
            jedis = this.getResource();
            if (jedis.exists(key)) {
                value = jedis.del(key);
                this.logger.debug("del {} = {}", key, value);
            }
        } catch (Exception e) {
            this.logger.error("del {} = {}", new Object[]{key, value, e});
        } finally {
            this.release(jedis);
        }
        return value;
    }

    public String get(String key) {
        String value = null;
        Jedis jedis = null;
        try {
            jedis = this.getResource();
            if (jedis.exists(key)) {
                value = jedis.get(key);
                value = StringUtils.isNotBlank(value) && !"nil".equalsIgnoreCase(value) ? value : null;
                this.logger.debug("get {} = {}", key, value);
            }
        } catch (Exception e) {
            this.logger.error("get {} = {}", new Object[]{key, value, e});
        } finally {
            this.release(jedis);
        }
        return value;
    }

    public String set(String key, String value) {
        return this.set(key, value, 0);
    }

    public String set(String key, String value, int expire) {
        Jedis jedis = null;
        String result = null;
        try {
            jedis = this.getResource();
            result = jedis.set(key, value);
            if (expire != 0) {
                jedis.expire(key, expire);
            }
            logger.debug("set {} = {}", key, value);
        } catch (Exception e) {
            logger.error("set {} = {}", new Object[]{key, value, e});
        } finally {
            this.release(jedis);
        }
        return result;
    }

    public boolean exists(String key) {
        boolean result = false;
        Jedis jedis = null;
        try {
            jedis = this.getResource();
            result = jedis.exists(key);
        } catch (Exception e) {
            logger.error("exists {}", key, e);
        } finally {
            this.release(jedis);
        }
        return result;
    }

    /**
     * ttl
     *
     * @param key
     * @return
     */
    public Long ttl(String key) {
        Long result = 0L;
        Jedis jedis = null;
        try {
            jedis = this.getResource();
            result = jedis.ttl(key);
        } catch (Exception e) {
            logger.error("exists {}", key, e);
        } finally {
            this.release(jedis);
        }
        return result;
    }

    public Long incr(String key) {
        return incrWithExpire(key, 0);
    }

    public Long incrWithExpire(String key, int seconds) {
        Jedis jedis = null;
        Long incr;
        try {
            if (StringUtils.isBlank(key)) {
                incr = null;
                return incr;
            }
            jedis = this.getResource();
            incr = jedis.incr(key);
            if (seconds > 0) {
                jedis.expire(key, seconds);
            }
        } catch (Exception e) {
            logger.error("incr {} ", key, e);
            return null;
        } finally {
            this.release(jedis);
        }
        return incr;
    }

    public Long incrBy(String key, Long integer) {
        Jedis jedis = null;
        Long incrBy;
        try {
            if (StringUtils.isBlank(key)) {
                incrBy = null;
                return incrBy;
            }
            if (integer == null) {
                integer = 0L;
            }
            jedis = this.getResource();
            incrBy = jedis.incrBy(key, integer);
        } catch (Exception e) {
            logger.error("incr {} ", key, e);
            return null;
        } finally {
            this.release(jedis);
        }
        return incrBy;
    }

    public Long incrByWithExpire(String key, Long integer, int seconds) {
        Jedis jedis = null;
        Long incrBy;
        try {
            if (StringUtils.isBlank(key)) {
                incrBy = null;
                return incrBy;
            }
            if (integer == null) {
                integer = 0L;
            }
            jedis = this.getResource();
            incrBy = jedis.incrBy(key, integer);

            if (seconds > 0) {
                jedis.expire(key, seconds);
            }
        } catch (Exception e) {
            logger.error("incr {} ", key, e);
            return null;
        } finally {
            this.release(jedis);
        }
        return incrBy;
    }

    public Long decrBy(String key, Long integer) {
        Jedis jedis = null;
        Long decrBy;
        try {
            if (StringUtils.isBlank(key)) {
                decrBy = null;
                return decrBy;
            }
            if (integer == null) {
                integer = 0L;
            }
            jedis = this.getResource();
            decrBy = jedis.decrBy(key, integer);
        } catch (Exception e) {
            logger.error("incr {} ", key, e);
            return null;
        } finally {
            this.release(jedis);
        }
        return decrBy;
    }

    public Long decr(String key) {
        Jedis jedis = null;
        Long decr;
        try {
            if (StringUtils.isBlank(key)) {
                decr = null;
                return decr;
            }
            jedis = this.getResource();
            decr = jedis.decr(key);
            return decr;
        } catch (Exception e) {
            logger.error("decr {} ", key, e);
            return null;
        } finally {
            this.release(jedis);
        }
    }

    /**
     * hgetAll
     *
     * @param key
     * @return
     */
    public Map<String, String> hgetAll(String key) {
        Map<String, String> map = new HashMap<>();
        Jedis jedis = null;
        try {
            if (StringUtils.isBlank(key)) {
                return map;
            }
            jedis = this.getResource();
            map = jedis.hgetAll(key);
            return map;
        } catch (Exception e) {
            logger.error("hgetAll {} ", key, e);
            return null;
        } finally {
            this.release(jedis);
        }
    }

    /**
     * hmset
     *
     * @param key
     * @return
     */
    public String hmset(String key, Map<String, String> map) {
        Jedis jedis = null;
        try {
            if (StringUtils.isBlank(key)) {
                return null;
            }
            jedis = this.getResource();
            return jedis.hmset(key, map);
        } catch (Exception e) {
            logger.error("hmset {} ", key, e);
            return null;
        } finally {
            this.release(jedis);
        }
    }

    /**
     * hdel
     *
     * @param key
     * @param fields
     * @return
     */
    public Long hdel(String key, String... fields) {
        Jedis jedis = null;
        try {
            jedis = this.getResource();
            return jedis.hdel(key, fields);
        } catch (Exception e) {
            logger.error("hdel {} ", key, e);
            return null;
        } finally {
            this.release(jedis);
        }
    }

    /**
     * llen(获取列表长度)
     *
     * @param key
     * @return
     */
    public Long llen(String key) {
        Jedis jedis = null;
        try {
            jedis = this.getResource();
            return jedis.llen(key);
        } catch (Exception e) {
            logger.error("llen {} ", key, e);
            return null;
        } finally {
            this.release(jedis);
        }
    }

    /**
     * lpushx(将一个值插入到已存在的列表头部)
     *
     * @param key
     * @param value
     * @return
     */
    public Long lpushx(String key, String value) {
        return lpush(key, value);
    }

    /**
     * lpush(将一个或多个值插入到列表头部)
     *
     * @param key
     * @param fields
     * @return
     */
    public Long lpush(String key, String... fields) {
        Jedis jedis = null;
        try {
            jedis = this.getResource();
            return jedis.lpush(key, fields);
        } catch (Exception e) {
            logger.error("lpush {} ", key, e);
            return null;
        } finally {
            this.release(jedis);
        }
    }

    /**
     * rpop(移除列表的最后一个元素,返回值为移除的元素)
     *
     * @param key
     * @return
     */
    public String rpop(String key) {
        Jedis jedis = null;
        try {
            jedis = this.getResource();
            return jedis.rpop(key);
        } catch (Exception e) {
            logger.error("rpop {} ", key, e);
            return null;
        } finally {
            this.release(jedis);
        }
    }


    /**
     * 获取分布式锁
     *
     * @param lockKey
     * @return
     */
    public boolean getLock(String lockKey) {
        return this.getLock(lockKey, "1", 5);
    }

    /**
     * 获取分布式锁
     *
     * @param lockKey
     * @param requestId
     * @param expireTime
     * @return
     */
    public boolean getLock(String lockKey, String requestId, int expireTime) {
        Jedis jedis = null;
        try {
            jedis = this.getResource();
            String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);
            if ("OK".equals(result)) {
                return true;
            }
            return false;
        } catch (Exception e) {
            logger.error("getLock {} ", lockKey, e);
            return false;
        } finally {
            this.release(jedis);
        }
    }

    /**
     * 释放分布式锁
     *
     * @param lockKey
     * @return
     */
    public boolean unLock(String lockKey) {
        return this.unLock(lockKey, "1");
    }

    /**
     * 释放分布式锁
     *
     * @param lockKey
     * @param requestId
     * @return
     */
    public boolean unLock(String lockKey, String requestId) {
        Jedis jedis = null;
        try {
            String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
                    "    return redis.call(\"del\",KEYS[1])\n" +
                    "else\n" +
                    "    return 0\n" +
                    "end";
            jedis = this.getResource();
            Object result = jedis.eval(script, Collections.singletonList(lockKey),
                    Collections.singletonList(requestId));
            if ("OK".equals(result)) {
                return true;
            }
            return false;
        } catch (Exception e) {
            logger.error("unLock {} ", lockKey, e);
            return false;
        } finally {
            this.release(jedis);
        }
    }

    private Jedis getResource() throws Exception {
        try {
            return jedisPool.getResource();
        } catch (Exception e) {
            logger.error("getResource.", e);
            throw e;
        }
    }

    private void release(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }
}

11. Las siguientes son las dos clases clave

ShiroConfiguration

package com.gane.maple.member.config.shiro;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Description TODO
 * @Date 2020/4/9 19:58
 * @Created by 王弘博
 */
@Configuration
public class ShiroConfiguration {

    @Value("${jedis.pool.host}")
    private String host;
    @Value("${jedis.pool.port}")
    private int port;
    @Value("${jedis.pool.password}")
    private String password;
    @Value("${jedis.pool.database}")
    private int database;

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setRealm(userRealm());
        defaultSecurityManager.setCacheManager(cacheManager());
        defaultSecurityManager.setSessionManager(sessionManager());
        defaultSecurityManager.setRememberMeManager(rememberMeManager());
        return defaultSecurityManager;
    }

    @Bean
    public UserShiroRealm userRealm() {
        UserShiroRealm userShiroRealm = new UserShiroRealm();
        userShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        userShiroRealm.setCachingEnabled(false);
        return userShiroRealm;
    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/user");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        //anon为不用登陆就可以访问的,authc 是必须登陆才可以访问, 
        //filterChainDefinitionMap.put("/**", "authc") 这个一定要放在最后
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/logout", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/index", "anon");
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 给static,不然Redis注入不进来
     *
     * @return
     */
    @Bean
    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions)
     * 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和
     * AuthorizationAttributeSourceAdvisor)即可实现此功能
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 用户注册的时候,程序将明文通过加密方式加密,存到数据库的是密文,
     * 登录时将密文取出来,再通过shiro将用户输入的密码进行加密对比,一样则成功,不一样则失败。
     *
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用md5算法;
        hashedCredentialsMatcher.setHashIterations(1024);//散列的次数,比如散列两次,相当于 md5( md5(""));
        return hashedCredentialsMatcher;
    }

    /**
     * cacheManager 缓存 redis实现
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    @Bean
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        redisManager.setPassword(password);
        redisManager.setDatabase(database);
        return redisManager;
    }

    /**
     * Session Manager
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

    /**
     * 记住我
     *
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        //rememberme cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位),通过以下代码可以获取
        //KeyGenerator keygen = KeyGenerator.getInstance("AES");
        //SecretKey deskey = keygen.generateKey();
        //System.out.println(Base64.encodeToString(deskey.getEncoded()));
        byte[] cipherKey = Base64.decode("wGiHplamyXlVB11UXWol8g==");
        cookieRememberMeManager.setCipherKey(cipherKey);
        cookieRememberMeManager.setCookie(rememberMeCookie());
        return cookieRememberMeManager;
    }

    @Bean
    public SimpleCookie rememberMeCookie() {
        //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //如果httyOnly设置为true,则客户端不会暴露给客户端脚本代码,使用HttpOnly cookie有助于减少某些类型的跨站点脚本攻击;
        simpleCookie.setHttpOnly(true);
        //记住我cookie生效时间,默认30天 ,单位秒:60 * 60 * 24 * 30
        simpleCookie.setMaxAge(60 * 60 * 7);
        return simpleCookie;
    }
}

UserShiroReaml

package com.gane.maple.member.config.shiro;

import com.gane.maple.member.model.User;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

/**
 * @Description UserShiroRealm
 * @Date 2020/4/9 19:59
 * @Created by 王弘博
 */
public class UserShiroRealm extends AuthorizingRealm {

    /**
     * 权限授权
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //表明当前登录者的角色(真实项目中这里会去查询DB,拿到用户的角色,存到redis里)
        info.addRole("admin");

        //表明当前登录者的角色(真实项目中这里会去查询DB,拿到该角色的资源权限,存到redis里)
        info.addStringPermission("admin:manage");

        return info;
    }

    /**
     * 登录认证
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

        //通过token去查询DB,获取用户的密码,这里密码直接写死
        User user = new User();
        user.setUsername(token.getUsername());

        return new SimpleAuthenticationInfo(user, "26bfdfe8689183e9235b1f0beb7a6f46",
                ByteSource.Util.bytes(user.getUsername()), getName());
    }


    /**
     * 密码(123456) + salt(maple),得出存进数据库里的密码:26bfdfe8689183e9235b1f0beb7a6f46
     *
     * @param args
     */
    public static void main(String[] args) {
        String hashAlgorithName = "MD5";
        String password = "123456";
        int hashIterations = 1024;//加密次数
        ByteSource credentialsSalt = ByteSource.Util.bytes("maple");
        SimpleHash simpleHash = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations);
        String s = simpleHash.toHex();
        System.out.println(s);
    }

}

Prueba:

index.html: se puede acceder sin iniciar sesión

user.html: requiere el inicio de sesión del usuario para acceder, si el usuario no ha iniciado sesión, vaya a la página de inicio de sesión login.html

user_manage.html: solo los usuarios con administrador: permiso de administración pueden acceder a la página

user_query.html: solo los usuarios con administrador: permiso de consulta pueden acceder a la página

(1) Inicie el proyecto y acceda directamente a  http: // localhost: 8080 / index , que debe ser accesible directamente

(2) Acceso directo a  http: // localhost: 8080 / user , debe saltar a la página de inicio de sesión

(3) Ingresamos maple / 111111, la contraseña es incorrecta, debemos permanecer en la página de inicio de sesión

(4), ingresamos maple / 123456, el inicio de sesión es exitoso, debe saltar a la página user.html

(5), visitamos  http: // localhost: 8080 / query , porque el usuario de arce no tiene este permiso, por lo que debe informar un error (porque no hice el manejo de excepciones aquí)

 (6), visitamos  http: // localhost: 8080 / manage , porque el usuario de arce tiene este permiso, por lo que deberíamos saltar a la página user_manage.html

Prueba completada, símbolo esperado

165 artículos originales publicados · Me gusta 103 · Visita 390,000+

Supongo que te gusta

Origin blog.csdn.net/qq_33101675/article/details/105440491
Recomendado
Clasificación