Shiro+SpringBoot+JPA

环境准备

Spring Boot 2.0.4.RELEASE
pom.xml

<dependency>      
    <groupId>org.springframework.boot</groupId>      
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>      
    <groupId>org.springframework.boot</groupId>      
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>     
    <groupId>org.apache.shiro</groupId>      
    <artifactId>shiro-core</artifactId>      
    <version>1.2.3</version>
</dependency>
<dependency>      
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.2.3</version>
</dependency>      
<dependency>          
    <groupId>com.alibaba</groupId>          
    <artifactId>druid</artifactId>          
    <version>1.0.20</version>      
</dependency>      
<dependency>          
    <groupId>org.apache.commons</groupId>         
    <artifactId>commons-lang3</artifactId>        
    <version>3.7</version>      
</dependency>     
<dependency>          
    <groupId>org.apache.commons</groupId>        
    <artifactId>commons-collections4</artifactId>         
    <version>4.1</version>      
</dependency>     
<dependency>          
    <groupId>org.springframework</groupId>   
    <artifactId>spring-context-support</artifactId> 
</dependency>   
<dependency>        
    <groupId>org.apache.tomcat.embed</groupId>  
    <artifactId>tomcat-embed-jasper</artifactId> 
</dependency>    
<dependency>          
    <groupId>javax.servlet</groupId>     
    <artifactId>javax.servlet-api</artifactId>   
</dependency>     
<dependency>          
    <groupId>javax.servlet</groupId>        
    <artifactId>jstl</artifactId>      
</dependency>     
<dependency>         
    <groupId>org.projectlombok</groupId>       
    <artifactId>lombok</artifactId>      
    <version>1.16.20</version>         
    <scope>provided</scope>   
</dependency>
<dependency>    
    <groupId>mysql</groupId>    
    <artifactId>mysql-connector-java</artifactId> 
    <scope>runtime</scope>
</dependency>
<dependency> 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>  
    <scope>test</scope>
</dependency>


数据库MySQL

`SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `permission`;CREATE TABLE `permission`  (  
`pid` int(11) NOT NULL COMMENT '权限id', 
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT 
NULL,  `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT 
NULL,  PRIMARY KEY (`pid`) USING BTREE) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci 
ROW_FORMAT = Compact;
INSERT INTO `permission` VALUES (1, 'add', NULL);INSERT INTO `permission` VALUES (2, 'delete', NULL);INSERT INTO `permission` VALUES (3, 'edit', NULL);INSERT INTO `permission` VALUES (4, 'query', NULL);
DROP TABLE IF EXISTS `permission_role`;CREATE TABLE `permission_role`  (  `id` int(11) NOT NULL AUTO_INCREMENT,  `rid` int(11) NOT NULL,  `pid` int(11) NOT NULL,  PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = 
utf8_general_ci ROW_FORMAT = Compact;
INSERT INTO `permission_role` VALUES (1, 1, 1);INSERT INTO `permission_role` VALUES (2, 1, 2);INSERT INTO `permission_role` VALUES (3, 1, 3);INSERT INTO `permission_role` VALUES (4, 1, 4);INSERT INTO `permission_role` VALUES (5, 2, 1);INSERT INTO `permission_role` VALUES (6, 2, 4);
DROP TABLE IF EXISTS `role`;CREATE TABLE `role`  (  `rid` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色id',  `rname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT 
NULL,  PRIMARY KEY (`rid`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = 
utf8_general_ci ROW_FORMAT = Compact;
INSERT INTO `role` VALUES (1, 'admin');INSERT INTO `role` VALUES (2, 'customer');
DROP TABLE IF EXISTS `user`;CREATE TABLE `user`  (  `uid` int(11) NOT NULL COMMENT '用户id',  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT 
NULL,  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT 
NULL,  PRIMARY KEY (`uid`) USING BTREE) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci 
ROW_FORMAT = Compact;-
NSERT INTO `user` VALUES (1, 'admin', '123');INSERT INTO `user` VALUES (2, 'demo', '123');-
DROP TABLE IF EXISTS `user_role`;CREATE TABLE `user_role`  (  `id` int(11) NOT NULL AUTO_INCREMENT,  `uid` int(11) NOT NULL,  `rid` int(11) NOT NULL,  PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = 
utf8_general_ci ROW_FORMAT = Compact;
INSERT INTO `user_role` VALUES (1, 1, 1);
INSERT INTO `user_role` VALUES (2, 2, 2);
SET FOREIGN_KEY_CHECKS = 1;

数据库表
image
image
image
image
image


配置文件
spring:  
    datasource:    
        type: com.alibaba.druid.pool.DruidDataSource    
        driver-class-name: com.mysql.jdbc.Driver    
        url: jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=UTF-8   
        username: root   
        password: root  
     mvc:   
        view:      
            prefix: /pages/      
            suffix: .jsp 
     jpa:    
        show-sql: true


创建自己的Realm 继承AuthorizingRealm

重写里面的两个方法
授权
doGetAuthorizationInfo(PrincipalCollection principalCollection)
从PrincipalCollection中取出User
创建一个List permissionList = new Arraylist<>();
获取到该用户对应的角色
获取到该角色下所有的权限
将权限名称添加到permissionList里
声明一个SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
将permissionList放入info里 info.addStringPermissions(permissionList);
最后将info返回
认证登陆方法
doGetAuthenticationInfo(AuthenticationToken token)
这个方法需要将我们传入的token转换成UsernamePasswordToken
然后从里面取出对应的username
再从数据库里取出对应的User
转换成SimpleAuthenticationInfo对象

public class AuthReaml extends AuthorizingRealm {    
    @Autowired    
    private UserRepository userRepository;    
    @Autowired    
    private UserRoleRepository userRoleRepository;    
    @Autowired    
    private RoleRepository roleRepository; 
    @Autowired    
    private PermissionRepository permissionRepository;    
    @Autowired    
    private PermissionRoleRepository permissionRoleRepository;
    
    /**     
    * 授权  
    * @param principalCollection 
    * @return    
    */    
    @Override    
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {        
        User user = (User) principalCollection.fromRealm(this.getClass().getName()).iterator().next();       
        List<String> permissionList = new ArrayList<>();       
        List<String> roleNameList = new ArrayList<>();   
        //根据用户id找到对应的角色        
        //定义角色集合        
        Set<Role> roleSet = new HashSet<>();        
        //从用户角色中间表查找到该用户下的所有角色        
        List<UserRole> userRoleList = userRoleRepository.findByUid(user.getUid());       
        //使用lambda取到该用户下所有的rid        
        List<Integer> rids = userRoleList.stream().map(UserRole::getRid).collect(Collectors.toList());       
        //获取数据库所有角色        
        List<Role> roleList = roleRepository.findAll();        
        for (Integer rid:rids){           
            for (Role role : roleList){                
                if (rid == role.getRid()){                    
                     //存入角色集合中                    
                    roleSet.add(role);               
                 }            
            }        
        }        
        //遍历角色集合        
        if(CollectionUtils.isNotEmpty(roleSet)){            
            //如果角色集合不为空,创建权限集合//           
            Set<Permission> permissionSet = new HashSet<>();        
            //通过角色id从权限角色中间表获取到所有的权限id           
            //1.先查询所有的角色权限集合            
            List<PermissionRole> permissionRoleList = permissionRoleRepository.findAll();           
            //2.通过判断角色id和权限id 获取该角色对应的所有权限id           
            //创建一个集合用来存放该角色对应的权限id            
            List<Integer> permissionIds = new ArrayList<>();           
            if (CollectionUtils.isNotEmpty(permissionRoleList)){                
            for (Role role:roleSet){                    
                roleNameList.add(role.getRname());                    
                for (PermissionRole permissionRole:permissionRoleList){                        
                    if (role.getRid()  == permissionRole.getRid()){                           
                    permissionIds.add(permissionRole.getPid());                        
                    }                   
                }                
            }    
        }       
            //通过权限id获取到所有的权限数据           
            //从数据库查找到所有的权限数据            
            List<Permission> permissions = permissionRepository.findAll();          
            if(CollectionUtils.isNotEmpty(permissionIds) && CollectionUtils.isNotEmpty(permissions)){             
                for (Integer permissionid : permissionIds){                  
                    for (Permission permission:permissions){                     
                        if (permissionid == permission.getPid()){                           
                        permissionList.add(permission.getName());                       
                        }                
                    }           
                }          
            }      
        }        
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();       
        info.addStringPermissions(permissionList);       
        info.addRoles(roleNameList);       
        return info;   
    }

    /** * 认证登陆
    * @param token
    * @return
    * @throws AuthenticationException 
    */
    @Overrideprotected 
    AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {   
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;    
        String username = usernamePasswordToken.getUsername();    
        User user = userRepository.findByUsername(username);    
        return new SimpleAuthenticationInfo(user,user.getPassword(),
        this.getClass().getName());
    }




密码校验功能重写 新建类CredentialMatcher 继承SimpleCredentialsMatcher
public class CredentialMatcher extends SimpleCredentialsMatcher {    
/**    
* 密码校验规则重写   
* @param token     
* @param info     
* @return     
*/    
@Override   
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {       
        UsernamePasswordToken usernamePasswordToken =  (UsernamePasswordToken) token;        
        //取出当前用户输入的密码        
        String password = new String(usernamePasswordToken.getPassword());       
        //获取数据库中该用户密码        
        String dbPassword = (String)info.getCredentials();        
        return this.equals(password,dbPassword);   
     }
}

创建Shiro配置类 ShiroConfiguration

@Qualifier 表示从spring中取出来的

@Configuration
public class ShiroConfiguration {    
    @Bean("shiroFilter")    
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager){        
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();       
        bean.setSecurityManager(securityManager);    
        //定义登陆url
        bean.setLoginUrl("/login"); 
        //登陆成功之后的url
        bean.setSuccessUrl("/index");       
        bean.setUnauthorizedUrl("/unauthorized");       
        LinkedHashMap<String,String> filterChainDefinitionMap = new LinkedHashMap<>();      
        //必须登陆才可以访问index
        filterChainDefinitionMap.put("/index","authc");   
        //不需要登陆就可以访问login
        filterChainDefinitionMap.put("/login","anon");       
        filterChainDefinitionMap.put("/loginUser","anon");
        //        filterChainDefinitionMap.put("/druid/**","anon"); //配置durid完全放行        
        filterChainDefinitionMap.put("/admin","roles[admin]"); //具有admin角色的用户才可以访问 /admin接口        
        filterChainDefinitionMap.put("/edit","perms[edit]"); //具有edit这个权限才可以访问 /edit接口        
        filterChainDefinitionMap.put("/**","user");        
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);      
        return bean;   
     }    
    @Bean("securityManager")    
    public SecurityManager securityManager(@Qualifier("authReaml") AuthReaml authReaml){        
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();       
        manager.setRealm(authReaml);        
        return manager;   
     }    
    @Bean("authReaml")    
    public AuthReaml authReaml(@Qualifier("credentialMatcher") CredentialMatcher matcher){        
        AuthReaml authReaml = new AuthReaml();        
        authReaml.setCredentialsMatcher(matcher);       
        return authReaml;   
     }    
    @Bean("credentialMatcher")    
    public CredentialMatcher credentialMatcher(){       
        return new CredentialMatcher();   
    }   
    //Shiro跟spring的关联
    @Bean    
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager){       
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();       
        advisor.setSecurityManager(securityManager);       
        return advisor;   
     }   
     
    @Bean    
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){        
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();        
        creator.setProxyTargetClass(true);        
        return creator;    
    }
}

创建Controller
@Controller
public class TestController {

    @RequestMapping("/login")
    public String login(){
        return "login";
    }
    @RequestMapping("/logOut")
    public String logOut(){
        Subject subject = SecurityUtils.getSubject();
        if (subject!=null){
            subject.logout();
        }
        return "login";
    }
    @RequestMapping("/index")
    public String index(){
        return "index";
    }
    @RequestMapping("/admin")
    @ResponseBody
    public String admin(){
        return "admin success";
    }
    @RequestMapping("/unauthorized")
    public String unauthorized(){
        return "unauthorized";
    }
    @RequestMapping("/edit")
    @ResponseBody
    public String edit(){
        return "edit success";
    }
    @RequestMapping("/loginUser")
    public String loginUser(@RequestParam("username") String username, @RequestParam("password") String password, HttpSession session){
        UsernamePasswordToken  token = new UsernamePasswordToken(username,password);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
            User user = (User) subject.getPrincipal();
            session.setAttribute("user",user);
            return "index";
        }catch (Exception e){
            return "login";
        }
    }
}

编写jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>    
<title>Login</title>
</head><body>
<h1>欢迎登陆</h1>
<form action="/loginUser" method="post">    
<input type="text" name="username"><br>   
<input type="password" name="password"><br>    
<input type="submit" value="提交"></form>
</body>
</html>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head> 
<title>Home</title>
</head>
<body><h1>欢迎登陆,${user.username}</h1>
</body>
</html>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>    
<title>unauthorized</title>
</head>
<body>
Unauthorized!
</body>
</html>

省略repository接口与实体类

总结:
优点:
提供了一套框架,而且这个框架可用,且易于使用
更灵活,应对需求能力强,Web能力强
可以与很多框架和应用进行集成
缺点:
除了自己实现RBAC外,操作的界面也需要自己实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值