springboot入门_shiro

shiro是apache的一款开元安全框架,可以进行用户身份的认证,授权,session管理,加密等。相对于springsecurity来说比较简单,容易理解和使用。下来我们实现在springboot项目中使用shiro框架。

我们使用RBAC(基于角色的访问控制)来设计角色权限关系,有3个实体:用户,角色,权限,在RBAC中用户拥有某一种角色,角色拥有一个或者多个资源,所以表结构设计如下:

 1 用户信息
 2   DROP TABLE IF EXISTS `user_info`;
 3   CREATE TABLE `user_info` (
 4     `id` int(11) NOT NULL AUTO_INCREMENT,
 5     `username` varchar(50) DEFAULT NULL,
 6     `password` varchar(32) DEFAULT NULL,
 7     `email` varchar(50) DEFAULT NULL,
 8     `usertype` char(1) DEFAULT NULL,
 9     PRIMARY KEY (`id`)
10  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
11  
12  角色信息
13  DROP TABLE IF EXISTS `role_info`;
14  CREATE TABLE `role_info` (
15    `id` int(11) NOT NULL AUTO_INCREMENT,
16    `rolename` varchar(50) DEFAULT NULL,
17    `roledesc` varchar(100) DEFAULT NULL,
18    PRIMARY KEY (`id`)
19  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
20  
21  用户角色关系
22  DROP TABLE IF EXISTS `user_role`;
23  CREATE TABLE `user_role` (
24    `id` int(11) NOT NULL AUTO_INCREMENT,
25    `userid` int(11) NOT NULL,
26    `roleid` int(11) NOT NULL,
27    PRIMARY KEY (`id`)
28  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
29  
30  资源(权限)信息
31  DROP TABLE IF EXISTS `resource_info`;
32  CREATE TABLE `resource_info` (
33    `id` int(11) NOT NULL AUTO_INCREMENT,
34    `resourceName` varchar(50) NOT NULL,
35    `resourceUrl` varchar(50) NOT NULL,
36    `resourceType` char(1) NOT NULL COMMENT '1-菜单;2-按钮',
37    `resourcePid` int(11) DEFAULT NULL COMMENT '上级,按钮上级为菜单',
38    PRIMARY KEY (`id`)
39  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
40  
41  角色资源关系
42  DROP TABLE IF EXISTS `role_resource`;
43  CREATE TABLE `role_resource` (
44    `id` int(11) NOT NULL AUTO_INCREMENT,
45    `roleId` int(11) NOT NULL,
46    `resourceId` int(11) NOT NULL,
47    PRIMARY KEY (`id`)
48  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

下来实现springboot项目与shiro的整合,创建springboot项目并使用mybatis做数据库访问,不清楚springboot整合mybatis的朋友可以看我之前的笔记,我们假设已经有了项目,这里只说整合shiro。

1 在pom文件中添加对shiro的依赖

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>1.4.0</version>
</dependency>

2 创建shiro配置信息类,完成配置

  1 @Configuration
  2 public class ShiroConfig {
  3     
  4     /**
  5      * shiro 过滤器
  6      * @param securityManager
  7      * @return
  8      */
  9     @Bean
 10     public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
 11         ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
 12         shiroFilter.setSecurityManager(securityManager);
 13         shiroFilter.setLoginUrl("/login");//登录失败后默认跳转到登录地址去
 14         shiroFilter.setSuccessUrl("/main");//登录成功后跳转地址
 15         
 16         /**
 17          * 过滤器链, 从前往后 顺序判断
 18          */
 19         Map<String, String> filterChain = new LinkedHashMap<String, String>();
 20         //记住我或者认证登录通过可以访问
 21         filterChain.put("/main", "user");
 22         
 23         filterChain.put("/js/**", "anon");//设置静态资源可以匿名访问
 24         filterChain.put("/logout", "logout");//logout shiro已经做了实现
 25         filterChain.put("/login", "anon");//可匿名访问
 26         
 27         filterChain.put("/**", "authc");// 除了上边配置的路径及资源外,其他访问都需要认证,此项必须放在最后
 28         shiroFilter.setFilterChainDefinitionMap(filterChain);
 29         
 30         shiroFilter.setUnauthorizedUrl("/unAuthPage");//没有授权的页面
 31         return shiroFilter;
 32     }
 33     
 34     /**
 35      * 安全管理器
 36      * @return
 37      */
 38     @Bean
 39     public DefaultWebSecurityManager securityManager(){
 40         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
 41         securityManager.setRealm(getShiroRealm());//自定义realm
 42         securityManager.setRememberMeManager(rememberMeManager());//记住我管理器
 43         return securityManager;
 44     }
 45     
 46     /**
 47      * 自定义的realm
 48      * @return
 49      */
 50     @Bean
 51     public ShiroRealm getShiroRealm(){
 52         ShiroRealm shiroRealm = new ShiroRealm();
 53         shiroRealm.setCredentialsMatcher(credentialsMatcher());//凭证匹配器
 54         return shiroRealm;
 55     }
 56     
 57     /**
 58      * 凭证匹配器 (密码的校验交给了shiro的SimpleAuthenticationInfo处理)
 59      * @return
 60      */
 61     @Bean
 62     public HashedCredentialsMatcher credentialsMatcher(){
 63         HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
 64         credentialsMatcher.setHashAlgorithmName("MD5");//散列算法名称,如:md5
 65         credentialsMatcher.setHashIterations(2);//散列算法的次数
 66         return credentialsMatcher;
 67     }
 68     
 69     /**
 70      * 记住我管理器
 71      * @return
 72      */
 73     @Bean
 74     public CookieRememberMeManager rememberMeManager(){
 75         CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
 76         rememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
 77         rememberMeManager.setCookie(cookie());
 78         return rememberMeManager;
 79     }
 80     
 81     /**
 82      * 记住我 cookie
 83      * @return
 84      */
 85     @Bean
 86     public SimpleCookie cookie(){
 87         SimpleCookie rememberMeCookie = new SimpleCookie("rememberMe");
 88         rememberMeCookie.setHttpOnly(true);
 89         rememberMeCookie.setMaxAge(60);//记住我生效时间,单位 秒
 90         return rememberMeCookie;
 91     }
 92     
 93     /**
 94      * shiro生命周期
 95      * @return
 96      */
 97     @Bean
 98     public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
 99         return new LifecycleBeanPostProcessor();
100     }
101     
102 }

2 自定义的realm,实现认证和授权

 1 public class ShiroRealm extends AuthorizingRealm {
 2     
 3     @Autowired
 4     private UserInfoService userInfoService;
 5     @Autowired
 6     private UserRoleService userRoleService;
 7     @Autowired
 8     private ResourceInfoService resourceInfoService;
 9 
10     /**
11      * 授权
12      */
13     @Override
14     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
15         SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
16         Set<String> permissionsSet = new HashSet<String>();//用来存放用户所拥有的权限资源
17         //当前用户
18         UserInfoEntity userInfo = (UserInfoEntity) principals.getPrimaryPrincipal();//如果认证方法中保存的是用户账号,此处获取的是用户的账号,如果存的是用户对象,则此处获取的是用户对象
19         //当前用户角色
20         UserRole userRole = userRoleService.selectByUserId(userInfo.getId());
21         if(userRole != null){
22             List<ResourceInfo> resourceList = resourceInfoService.selectByRoleId(userRole.getRoleId());
23             if(resourceList != null && resourceList.size()>0){
24                 for(int i=0; i<resourceList.size(); i++){
25                     ResourceInfo ri = resourceList.get(i);
26                     permissionsSet.add(ri.getResourceUrl());
27                 }
28             }
29         }
30         info.setStringPermissions(permissionsSet);
31         return info;
32     }
33 
34     /**
35      * 认证
36      */
37     @Override
38     protected AuthenticationInfo doGetAuthenticationInfo(
39             AuthenticationToken authToken) throws AuthenticationException {
40         //将AuthenticationToken转换为UsernamePasswordToken
41         UsernamePasswordToken token = (UsernamePasswordToken) authToken;
42         //获取用户名
43         String username = token.getUsername();
44         //判断当前用户名在数据库中是否存在
45         UserInfoEntity userInfo = userInfoService.selectByEmail(username);
46         if(userInfo == null){
47             throw new UnknownAccountException("账户不存在");
48         }
49         //从数据库对象获取的密码
50         String password = userInfo.getPassword();
51         
52         //认证的实体信息,可以是用户实体,也可以是用户账号
53         Object principal = userInfo;
54         //认证的密码
55         Object credentials = password;
56         //加密的颜
57         ByteSource credentialsSalt = ByteSource.Util.bytes(username);//使用登录的账号(唯一性)作为颜
58         
59         //通过SimpleAuthenticationInfo 认证
60         SimpleAuthenticationInfo authInfo = null;
61         authInfo = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, this.realmName());
62         return authInfo;
63     }
64     
65     public String realmName(){
66         return "shiroRealm";
67     }
68 }

3 登录实现

 1 @Controller
 2 public class LoginController {
 3     
 4     private static final Logger logger = LogManager.getLogger(LoginController.class);
 5     
 6     @RequestMapping("/login")
 7     public String login(){
 8         logger.info("跳转至登录页。。。");
 9         return "login";
10     }
11     
12     @RequestMapping(value="/login",method=RequestMethod.POST)
13     @ResponseBody
14     public String doLogin(@RequestParam("username") String username, @RequestParam("password") String password, 
15             @RequestParam(value = "rememberMeParam", required = false) String rememberMeParam, Map<String, Object> map){
16         UsernamePasswordToken token = new UsernamePasswordToken(username, password);
17         
18         //记住我
19         if("Y".equals(rememberMeParam)){
20             token.setRememberMe(true);
21         }
22         
23         Subject subject = SecurityUtils.getSubject();
24         try {
25             subject.login(token);
26         } catch (UnknownAccountException e) {
27             //e.printStackTrace();
28             logger.info("账号不存在");
29         } catch (IncorrectCredentialsException e) {
30             //e.printStackTrace();
31             logger.info("密码不正确");
32         }
33         
34         if(subject.isAuthenticated()){
35             map.put("result", "success");
36             logger.info("登录成功。。。");
37         }else{
38             map.put("result", "faile");
39             map.put("msg", "账号或密码错误");
40         }
41         return JSON.toJSONString(map);
42     }
43     
44     @RequestMapping("/main")
45     public String main(Model model){
46         Subject subject = SecurityUtils.getSubject();
47         UserInfoEntity userInfo = (UserInfoEntity) subject.getPrincipal();
48         model.addAttribute("currentUser", userInfo);
49         return "main";
50     }
51     
52     @RequestMapping("/unAuthPage")
53     public String unAuthPage(){
54         return "unAuth";
55     }
56     
57     @RequestMapping("/logout")
58     public String logout(){
59         Subject subject = SecurityUtils.getSubject();
60         subject.logout();
61         return "redirect:/login";
62     }
63 
64 }

4 退出 退出功能shiro已经为我做了实现,我们只需要调用Subject的logout()方法即可实现用户退出。

5 测试 

在没有登录的情况下,如果我们访问main方法,会被shiroFilter拦截,并将请求转向loginUrl方法引导用户登录;登录成功后会跳转至successUrl;

在页面上我们使用shiro的标签来做权限过滤,需要在页面引入shiro标签库:<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>,可以在菜单或者按钮上加上权限过滤标签如:

<shiro:hasPermission name="your perMission">
  <a href=".....">用户管理</a>
</shiro:hasPermission>

 

在加载页面时,遇到shiro标签时会去后台查询用户权限信息,如果用户拥有当前标签中name属性指定的权限信息时会加载并显示当前shiro标签包含的页面元素信息,否则不会显示

 

posted @ 2018-07-13 13:21  Allen_Wl  阅读(599)  评论(0编辑  收藏  举报