转载

Spring Boot 整合Shiro 最新 最全面(Mybatis版本)

1: 依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>${thymeleaf-shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
       <dependency>
           <groupId>org.apache.shiro</groupId>
           <artifactId>shiro-spring</artifactId>
           <version>1.5.1</version>
      </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.xmlunit</groupId>
            <artifactId>xmlunit-core</artifactId>
        </dependency>
    </dependencies>

2: application.xml配置

#server.servlet.context-path=/hello
##端口号
server.port=8888

##检查 mybatis 配置是否存在,一般命名为 mybatis-config.xml
mybatis.check-config-location =true
##配置文件位置 Resource下mybaits文件夹
mybatis.config-location=classpath:mybatis/mybatis-config.xml
## mapper xml 文件地址  Resource下mapper文件夹
mybatis.mapper-locations=classpath*:mapper/*Mapper.xml
##日志级别 com.yang.dao 为包名
#logging.level.com.dgw.springbootandshiro.dao=debug
##数据库url
spring.datasource.url=jdbc:mysql://localhost/rbac?userSSL=true&useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT
##数据库用户名
spring.datasource.username=root
##数据库密码
spring.datasource.password=root
##数据库驱动
spring.datasource.driver-class-name=com.mysql.jdbc.Driver


spring.thymeleaf.check-template-location=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
# 建议在开发时关闭缓存,不然没法看到实时页面
spring.thymeleaf.cache=false
##去除thymeleaf的html严格校验
spring.thymeleaf.mode=HTML

spring.main.allow-bean-definition-overriding=true

#debug=true
#简单设置一下日志等级
logging.level.web=info
logging.level.root=info

3: Mybatis 配置:

目录结构如下:

Spring Boot 整合Shiro 最新 最全面(Mybatis版本)

Mybatis 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL Map Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <typeAlias alias="Integer" type="java.lang.Integer" />
        <typeAlias alias="Long" type="java.lang.Long" />
        <typeAlias alias="HashMap" type="java.util.HashMap" />
        <typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />
        <typeAlias alias="ArrayList" type="java.util.ArrayList" />
        <typeAlias alias="LinkedList" type="java.util.LinkedList" />
        <typeAlias alias="user" type="com.dgw.springbootandshiro.bean.User"/>
    </typeAliases>
</configuration>

实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String username;
    private String password;
    private String salt;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
    private Integer id;
    private String roleName;
    private Date createTime;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permission {
    private Integer id;
    private String permissionName;
    private Date createTime;
}

dao接口: 部分 其余扎着框框画鸭蛋

@Mapper
public interface UserMapper {
    User queryUserByUsername(@Param("username") String username);
    Integer insertUser(User user);
}

services实现:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userDAO;

    @Override
    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = SQLException.class)
    public User queryUserByUsername(String username) {
        return userDAO.queryUserByUsername(username);
    }

    @Override
    public Integer insertUser(User user) {
        // 加密
        String salt = UUID.randomUUID().toString();
        String s = new Sha256Hash(user.getPassword(), salt, MyConstant.INTERCOUNT).toBase64();
        // 设置密文
        user.setPassword(s);
        // 设置盐
        user.setSalt(salt);
        return userDAO.insertUser(user);
    }
}

3: Shiro配置

Realm配置i

public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermissionService permissionService;
    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String  username  = (String)principalCollection.getPrimaryPrincipal();
        // 查询当前用户的权限信息
        Set<String> roles = roleService.queryAllRolenameByUsername(username);
        Set<String> perms = permissionService.queryAllPermissionByUsername(username);
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
        simpleAuthorizationInfo.setStringPermissions(perms);

        return simpleAuthorizationInfo;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String usernmae = (String) authenticationToken.getPrincipal();
        User user = userService.queryUserByUsername(usernmae);
        if (user == null) {
            return null;
        }
        //这里会去校验密码是否正确
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user.getUsername(),
                user.getPassword(),
                ByteSource.Util.bytes(user.getSalt()),
                getName()
        );
        return authenticationInfo;
    }
}

Shiroconfig 配置

@Configuration
public class ShiroConfig {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Bean("shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shirFilter(@Qualifier("securityManager") SecurityManager securityManager) {
        logger.info("启动shiroFilter--时间是:" + new Date());
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //shiro拦截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->
        Map<String, Filter> filterMap=new LinkedHashMap<String, Filter>();
        filterMap.put("MyRememberFilter", new MyRememberFilter());

        shiroFilterFactoryBean.setFilters(filterMap);

        // 如果不设置默认会自动寻找Web工程根目录下的"/login"页面,即本文使用的login.html
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/main");
        //错误页面,认证不通过跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        //未授权界面
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");


        // 配置不被拦截的资源及链接
        filterChainDefinitionMap.put("/static/**", "anon");
        // 退出过滤器
        filterChainDefinitionMap.put("/logout", "logout");
        //开启注册页面不需要权限
        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/user/register", "anon");

        //配置需要认证权限的
        filterChainDefinitionMap.put("/**", "authc");


        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 配置shiro的生命周期
     *
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 定义加密规则 存入密码时也必须加密
     */
    @Bean
    public HashedCredentialsMatcher myMatcher(){
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("SHA-256");
        // true means hex encoded, false means base64 encoded
        matcher.setStoredCredentialsHexEncoded(false);
        matcher.setHashIterations(10000);
        return  matcher;
    }

    public class MyRememberFilter extends FormAuthenticationFilter {
        protected boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response, Object mappedValue){
            Subject subject=getSubject(request,response);
            if(!subject.isAuthenticated() && subject.isRemembered()){
                if(subject.getSession().getAttribute("user")==null &&subject.getPrincipal()!=null){
                    subject.getSession().setAttribute("user",subject.getPrincipal());
                }

            }
            return subject.isAuthenticated() || subject.isRemembered();
        }
    }
   /* private class MyMatcher extends HashedCredentialsMatcher {
        @Override
        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
            UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
            String pwd = encrypt(String.valueOf(usernamePasswordToken.getPassword()));
            String mysqlpwd = (String) info.getCredentials();
            logger.info("密码"+mysqlpwd);
            logger.info("密码"+pwd);
            return this.equals(pwd, mysqlpwd);
        }

        //将传进来的密码进行加密的方法
        private String encrypt(String data) {
            // 加密
            String salt = UUID.randomUUID().toString();
            String s = new Sha256Hash(data, salt, MyConstant.INTERCOUNT).toBase64();
            return s;
        }
    }*/

    /**
     * 自定义身份认证Realm(包含用户名密码校验,权限校验等)
     */
    @Bean
    public ShiroRealm myShiroRealm() {
        ShiroRealm myShiroRealm = new ShiroRealm();
        myShiroRealm.setCredentialsMatcher(myMatcher());
        return myShiroRealm;
    }

    /**
     * 使用shiro 支持thymeleaf 模版引擎
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    /**
     * 配置记住我Cookie对象参数,rememberMeCookie()方法是设置Cookie的生成模版
     */
    public SimpleCookie rememberMeCookie() {
        //这个参数是cookie的名称,对应前端的checkbox的name=rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //cookie生效时间为10秒
        simpleCookie.setMaxAge(100000);
        return simpleCookie;
    }

    /**
     * 配置Cookie管理对象,rememberMeManager()方法是生成rememberMe管理器
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        return cookieRememberMeManager;
    }

    /**
     * 配置sessionCookie对象参数,sessionCookie()方法是设置Cookie的生成模版
     */
    public SimpleCookie sessionIdCookie() {
        //这个参数是cookie的名称,对应前端的checkbox的name=rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("JSESSIONID");
        //只允许http请求访问cookie
        simpleCookie.setHttpOnly(true);
        //cookie生效时间为10秒 默认为-1
        simpleCookie.setMaxAge(-1);
        return simpleCookie;
    }

    /**
     * 配置Cookie管理对象,rememberMeManager()方法是生成rememberMe管理器
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager webSessionManager = new DefaultWebSessionManager();
        webSessionManager.setSessionIdCookie(sessionIdCookie());
        // session全局超时时间, 单位:毫秒 ,30分钟 默认值为1800000
        webSessionManager.setGlobalSessionTimeout(1800000);
        //开启检测器,默认开启
        webSessionManager.setSessionIdUrlRewritingEnabled(true);
        // 检测间隔事件 时间为1小时
        webSessionManager.setSessionValidationInterval(3600000);
        // 设置监听器
        //webSessionManager.setSessionListeners();
        return webSessionManager;
    }

    @Bean
    public CacheManager cacheManager() {
        return new MemoryConstrainedCacheManager();
    }

    @Bean(name = "securityManager")
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //注入自定义myRealm
        securityManager.setRealm(myShiroRealm());
        //注入自定义cacheManager
        securityManager.setCacheManager(cacheManager());
        //注入记住我管理器
        securityManager.setRememberMeManager(rememberMeManager());
        //注入自定义sessionManager
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }


    //开启shiro aop注解支持,不开启的话权限验证就会失效
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

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

    //配置异常处理,不配置的话没有权限后台报错,前台不会跳转到403页面
    @Bean(name = "simpleMappingExceptionResolver")
    public SimpleMappingExceptionResolver
    createSimpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
        Properties mappings = new Properties();
        mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理
        mappings.setProperty("UnauthorizedException", "403");
        simpleMappingExceptionResolver.setExceptionMappings(mappings);  // None by default
        simpleMappingExceptionResolver.setDefaultErrorView("403");    // No default
        simpleMappingExceptionResolver.setExceptionAttribute("ex");     // Default is "exception"
        return simpleMappingExceptionResolver;
    }
}

4: thymeleaf

 login.html
<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
      xmlns:th="http://www.thymeleaf.org" >
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Insert title here</title>
</head>
<body>
<form method="post" action="login" >
    <label for="name">用户名</label>
    <shiro:authenticated>
        <input id="name" type="text" name="username" value="<shiro:principal/>" >
    </shiro:authenticated>
    <shiro:notAuthenticated>
        <input id="name" type="text" name="username" value=" " >
    </shiro:notAuthenticated>
    <br>
    <label for="pass">密码</label>
    <input id="pass" type="text" name="password">
    <button type="submit">提交</button>

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

5 controller

@Controller
@RequestMapping("/user")
public class LoginController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private UserService userService;

    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @PostMapping("/login")
    public String loginLogic(User user) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
        // 登录失败会抛出异常,则交由异常解析器处理
        token.setRememberMe(true);
        subject.login(token);

        return "main";
    }

    @GetMapping("/register")
    public String regiter() {
        return "register";
    }


    @PostMapping("/register")
    public String logicRegiter(User user) {
        userService.insertUser(user);
        return "redirect:login";
    }
}
原文  http://www.cnblogs.com/dgwblog/p/12584884.html
正文到此结束
Loading...