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-a16ba8-1558117658470)]

[图片上传失败...(image-d4b9e7-1558117658470)]

[图片上传失败...(image-f03df9-1558117658470)]

[图片上传失败...(image-6f0a88-1558117658470)]

[图片上传失败...(image-89de60-1558117658470)]


配置文件


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<String> 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外,操作的界面也需要自己实现
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容