SpringBoot集成Shiro思路以及代码过程

模型 
假设存在用户(User)、角色(Role)、权限(Permission)3中模型。依赖关系如下:
用户分配角色,角色分配权限。用户不直接参与权限的分配。

SpringBoot集成Shiro流程 
demo的代码架构如下:


创建IO模型

User存在:1、唯一ID 2、用户名 3、密码 4、拥有的Role

public class User {

private Integer uid ;

private String username ;

private String password ;

private Set<Role> roles = new HashSet<>() ;
}

Role存在:1、唯一ID 2、角色名 3、角色拥有的权限 4、拥有该角色的用户(可以不需要)
public class Role {

private Integer rid ;

private String rname ;

private Set<Permission> permissions = new HashSet<>() ;

private Set<User> users = new HashSet<>() ;
}

Permission存在:1、唯一ID 2、权限名 3、。。。
public class Permission {

private Integer pid ;

private String name ;

private String url ;
}

数据库sql如下:
-- 权限表 --
CREATE TABLE permission (
pid int(11) NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL DEFAULT '',
url VARCHAR(255) DEFAULT '',
PRIMARY KEY (pid)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

INSERT INTO permission VALUES ('1', 'add', '');
INSERT INTO permission VALUES ('2', 'delete', '');
INSERT INTO permission VALUES ('3', 'edit', '');
INSERT INTO permission VALUES ('4', 'query', '');

-- 用户表 --
CREATE TABLE user(
uid int(11) NOT NULL AUTO_INCREMENT,
username VARCHAR(255) NOT NULL DEFAULT '',
password VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (uid)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

INSERT INTO user VALUES ('1', 'admin', '123');
INSERT INTO user VALUES ('2', 'demo', '123');

-- 角色表 --
CREATE TABLE role(
rid int(11) NOT NULL AUTO_INCREMENT,
rname VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (rid)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

INSERT INTO role VALUES ('1', 'admin');
INSERT INTO role VALUES ('2', 'customer');

-- 权限角色关系表 --
CREATE TABLE permission_role (
rid int(11) NOT NULL ,
pid int(11) NOT NULL ,
KEY idx_rid (rid),
KEY idx_pid (pid)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

INSERT INTO permission_role VALUES ('1', '1');
INSERT INTO permission_role VALUES ('1', '2');
INSERT INTO permission_role VALUES ('1', '3');
INSERT INTO permission_role VALUES ('1', '4');
INSERT INTO permission_role VALUES ('2', '1');
INSERT INTO permission_role VALUES ('2', '4');

-- 用户角色关系表 --
CREATE TABLE user_role (
uid int(11) NOT NULL ,
rid int(11) NOT NULL ,
KEY idx_uid (uid),
KEY idx_rid (rid)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

INSERT INTO user_role VALUES (1, 1);
INSERT INTO user_role VALUES (2, 2);

Mapper的配置
1、UserMapper
根据用户名去数据库查找实体
public interface UserMapper {

User findByUsername ( @Param ( "username" ) String username) ;
}

2、service配置 存在如下关系



3、mapper.xml
mapper中配置好各个IO的对应情况


需要配置读取UserMapper.xml的路径


application中需要配置需要加载的mapper类路径


授权与鉴权
需要继承 import org.apache.shiro.realm.AuthorizingRealm ; 实现当中的方法。


1、第一个方法为授权
2、第二个方法为登陆

1、授权:
核心思想:
  • 获取当前用户信息
  • 因为user存放的是set<Role>,所以用for循环取出内容
  • 取出的role存放new 出来的List中
  • 利用for循环取出permission放入new出来的List中
  • 将刚才的list放入 new SimpleAuthorizationInfo() 中的role和permission中
  • 返回该对象
@Override
protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principals)
{
// 获取 User 用户
User user = (User) principals.fromRealm( this .getClass().getName()).iterator().next() ;
List<String> permissionList = new ArrayList<>() ;
List<String> roleNameList = new ArrayList<>() ;
Set<Role> roleSet = user.getRoles() ;
if (roleSet != null )
{
for (Role role : roleSet)
{
roleNameList.add(role.getRname()) ;
Set<Permission> permissionSet = role.getPermissions() ;
if (permissionSet != null )
{
for (Permission permission : permissionSet)
{
permissionList.add(permission.getName()) ;
}
}
}
}

// 需要把角色和权限放入 info
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo() ;

// 权限设定
info.addStringPermissions(permissionList) ;

// 角色设定
info.addRoles(roleNameList) ;
return info ;
}

1、登陆:
核心思想:
  • token中获取当前用户信息
  • 因为user存放的是set<Role>,所以用for循环取出内容
  • 取出用户名。用户名和密码是byte存放,可以考虑用new String转换。
  • 返回SimpleAuthenticationInfo对象。包括用户实体,密码,该对象名
@Override
protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken token) throws AuthenticationException
{
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token ;
String username = usernamePasswordToken.getUsername() ;
User user = userService .findByUsername(username) ;
return new SimpleAuthenticationInfo(user , user.getPassword() , this .getClass().getName()) ;
}


校验重写设定
继承 SimpleCredentialsMatcher 重写校验方法
校验方法可以自己设定,例如:加密盐(具体需要网上看教程)
这里只是做简单的对比并且返回对比结果。

public class CredentialMatcher extends SimpleCredentialsMatcher
{
@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) ;
}
}

ShiroConfiguration的配置

@Configuration
public class ShiroConfiguration
{
@Bean ( "shiroFilter" )
public ShiroFilterFactoryBean shiroFilter ( @Qualifier ( "securityManager" ) SecurityManager manager)
{
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean() ;
bean.setSecurityManager(manager) ;

// 登陆界面
bean.setLoginUrl( "/login" ) ;

// 成功登陆后的界面
bean.setSuccessUrl( "/index" ) ;

// 没有权限访问的界面
bean.setUnauthorizedUrl( "/unauthorized" ) ;

// 定制相关表单是否需要相关权限的设定,具体配置信息请看:Shiro-内置的FilterChain
LinkedHashMap<String , String> filterChainDefinitionMap = new LinkedHashMap<>() ;

 //注意过滤器配置顺序 不能颠倒这里是按照顺序判断的

// index 界面需要鉴权
filterChainDefinitionMap.put( "/index" , "authc" ) ;

// login loginUser 表单不需要验证
filterChainDefinitionMap.put( "/login" , "anon" ) ;
filterChainDefinitionMap.put( "/loginUser" , "anon" ) ;

// admin 表单需要角色 admin 才能访问
filterChainDefinitionMap.put( "/admin" , "roles[admin]" ) ;

// edit 表单需要权限 edit 才能访问
filterChainDefinitionMap.put( "/edit" , "perms[edit]" ) ;
filterChainDefinitionMap.put( "/druid/**" , "anon" ) ;
filterChainDefinitionMap.put( "/**" , "user" ) ;
bean.setFilterChainDefinitionMap(filterChainDefinitionMap) ;

return bean ;
}

@Bean ( "securityManager" )
public SecurityManager securityManager ( @Qualifier ( "authRealm" ) AuthRealm authRealm)
{
DefaultWebSecurityManager manager = new DefaultWebSecurityManager() ;
manager.setRealm(authRealm) ;
return manager ;
}

@Bean ( "authRealm" )
public AuthRealm authRealm ()
{
AuthRealm authRealm = new AuthRealm() ;

/**
* shiro 自带的 MemoryConstrainedCacheManager 作缓存只能用于本机,那么在集群时就无法使用,
* 如果使用 ehcache redis 等其他缓存,可以参考 https://www.cnblogs.com/lic309/p/4072848.html
*/
authRealm.setCacheManager( new MemoryConstrainedCacheManager()) ;

// com.mmall.demo2.CredentialMatcher 中自定义的密码比较器对密码进行比较
authRealm.setCredentialsMatcher( new CredentialMatcher()) ;
return authRealm ;
}


// shiro spring 进行绑定
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor ( @Qualifier ( "securityManager" ) SecurityManager securityManager)
{
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor() ;
advisor.setSecurityManager(securityManager) ;
return advisor ;
}

// 开启自动代码,设置为 true 即可
@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 ( "/index" )
public String index ()
{
return "index" ;
}

@RequestMapping ( "/logout" )
public String logout ()
{
// 先验证主体
Subject subject = SecurityUtils. getSubject () ;
if (subject != null )
{
subject.logout() ;
}
return "login" ;
}

@RequestMapping ( "unauthorized" )
public String unauthorized ()
{
return "unauthorized" ;
}

@RequestMapping ( "/admin" )
@ResponseBody
public String admin ()
{
return "admin success" ;
}

@RequestMapping ( "/edit" )
@ResponseBody
public String edit ()
{
return "edit success" ;
}

@RequestMapping ( "/loginUser" )
public String loginUser ( @RequestParam ( "username" ) String username ,
@RequestParam ( "password" ) String password ,
HttpSession session)
{
// 初始化这个用户的 token
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" ;
}
}
}

application.properties代码
##蓝色加粗字体需自行修改
## database ##
spring.datasource.type = com . alibaba . druid . pool . DruidDataSource
spring.datasource.driver-class-name = com . mysql . jdbc . Driver
spring.datasource.url = jdbc:mysql://localhost:3306/ fys ?characterEncoding=UTF-8
spring.datasource.username = root
spring.datasource.password = root
## mybatis ##
mybatis.mapper-locations = mappers/*.xml
#mybatis.type-aliases-package=com.mmall.demo2.model
## jsp ##
spring.mvc.view.prefix = /pages/
spring.mvc.view.suffix = .jsp


index.jsp
<%@ page contentType=" text/html;charset=UTF-8 " language=" java " %>
<html>
<head>
<title> Home </title>
</head>
<body>

<h1> 欢迎登录 , ${ user.username } </h1>
</body>
</html>

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>

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



FilerChain的方法解释:

  1. /** 
  2.  * Shiro-1.2.2内置的FilterChain 
  3.  * @see ============================================================================================================================= 
  4.  * @see 1)Shiro验证URL时,URL匹配成功便不再继续匹配查找(所以要注意配置文件中的URL顺序,尤其在使用通配符时) 
  5.  * @see   故filterChainDefinitions的配置顺序为自上而下,以最上面的为准 
  6.  * @see 2)当运行一个Web应用程序时,Shiro将会创建一些有用的默认Filter实例,并自动地在[main]项中将它们置为可用 
  7.  * @see   自动地可用的默认的Filter实例是被DefaultFilter枚举类定义的,枚举的名称字段就是可供配置的名称 
  8.  * @see   anon---------------org.apache.shiro.web.filter.authc.AnonymousFilter 
  9.  * @see   authc--------------org.apache.shiro.web.filter.authc.FormAuthenticationFilter 
  10.  * @see   authcBasic---------org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter 
  11.  * @see   logout-------------org.apache.shiro.web.filter.authc.LogoutFilter 
  12.  * @see   noSessionCreation--org.apache.shiro.web.filter.session.NoSessionCreationFilter 
  13.  * @see   perms--------------org.apache.shiro.web.filter.authz.PermissionAuthorizationFilter 
  14.  * @see   port---------------org.apache.shiro.web.filter.authz.PortFilter 
  15.  * @see   rest---------------org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter 
  16.  * @see   roles--------------org.apache.shiro.web.filter.authz.RolesAuthorizationFilter 
  17.  * @see   ssl----------------org.apache.shiro.web.filter.authz.SslFilter 
  18.  * @see   user---------------org.apache.shiro.web.filter.authz.UserFilter 
  19.  * @see ============================================================================================================================= 
  20.  * @see 3)通常可将这些过滤器分为两组 
  21.  * @see   anon,authc,authcBasic,user是第一组认证过滤器 
  22.  * @see   perms,port,rest,roles,ssl是第二组授权过滤器 
  23.  * @see   注意user和authc不同:当应用开启了rememberMe时,用户下次访问时可以是一个user,但绝不会是authc,因为authc是需要重新认证的 
  24.  * @see                      user表示用户不一定已通过认证,只要曾被Shiro记住过登录状态的用户就可以正常发起请求,比如rememberMe 
  25.  * @see                      说白了,以前的一个用户登录时开启了rememberMe,然后他关闭浏览器,下次再访问时他就是一个user,而不会authc 
  26.  * @see ============================================================================================================================= 
  27.  * @see 4)举几个例子 
  28.  * @see   /admin=authc,roles[admin]      表示用户必需已通过认证,并拥有admin角色才可以正常发起'/admin'请求 
  29.  * @see   /edit=authc,perms[admin:edit]  表示用户必需已通过认证,并拥有admin:edit权限才可以正常发起'/edit'请求 
  30.  * @see   /home=user                     表示用户不一定需要已经通过认证,只需要曾经被Shiro记住过登录状态就可以正常发起'/home'请求 
  31.  * @see ============================================================================================================================= 
  32.  * @see 5)各默认过滤器常用如下(注意URL Pattern里用到的是两颗星,这样才能实现任意层次的全匹配) 
  33.  * @see   /admins/**=anon             无参,表示可匿名使用,可以理解为匿名用户或游客 
  34.  * @see   /admins/user/**=authc       无参,表示需认证才能使用 
  35.  * @see   /admins/user/**=authcBasic  无参,表示httpBasic认证 
  36.  * @see   /admins/user/**=user        无参,表示必须存在用户,当登入操作时不做检查 
  37.  * @see   /admins/user/**=ssl         无参,表示安全的URL请求,协议为https 
  38.  * @see   /admins/user/**=perms[user:add:*] 
  39.  * @see       参数可写多个,多参时必须加上引号,且参数之间用逗号分割,如/admins/user/**=perms["user:add:*,user:modify:*"] 
  40.  * @see       当有多个参数时必须每个参数都通过才算通过,相当于isPermitedAll()方法 
  41.  * @see   /admins/user/**=port[8081] 
  42.  * @see       当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString 
  43.  * @see       其中schmal是协议http或https等,serverName是你访问的Host,8081是Port端口,queryString是你访问的URL里的?后面的参数 
  44.  * @see   /admins/user/**=rest[user] 
  45.  * @see       根据请求的方法,相当于/admins/user/**=perms[user:method],其中method为post,get,delete等 
  46.  * @see   /admins/user/**=roles[admin] 
  47.  * @see       参数可写多个,多个时必须加上引号,且参数之间用逗号分割,如/admins/user/**=roles["admin,guest"] 
  48.  * @see       当有多个参数时必须每个参数都通过才算通过,相当于hasAllRoles()方法 
  49.  * @see ============================================================================================================================= 
  50.  * @create Sep 29, 2013 5:24:49 PM 
  51.  * @author 玄玉<http://blog.csdn.net/jadyer> 
  52.  */  


我截取一段关于shiro.xml中关于 ShiroFilterFactoryBean配置的 说明,如下:
 
securityManager:这个属性是必须的。
 
loginUrl:没有登录的用户请求需要登录的页面时自动跳转到登录页面,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面。
 
successUrl:登录成功默认跳转页面,不配置则跳转至”/”。如果登陆前点击的一个需要登录的页面,则在登录自动跳转到那个需要登录的页面。不跳转到此。
 
unauthorizedUrl:没有权限默认跳转的页面。 
 
过滤器简称
对应的java类
anon
org.apache.shiro.web.filter.authc.AnonymousFilter
authc
org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic
org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
perms
org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port
org.apache.shiro.web.filter.authz.PortFilter
rest
org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles
org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl
org.apache.shiro.web.filter.authz.SslFilter
user
org.apache.shiro.web.filter.authc.UserFilter
logout
org.apache.shiro.web.filter.authc.LogoutFilter
   
anon:例子/admins/**=anon 没有参数,表示可以匿名使用。
 
authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数
 
roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
 
perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
 
rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
 
port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString
 
是你访问的url里的?后面的参数。
 
authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证
 
ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
 
user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
 
注:anon,authcBasic,auchc,user是认证过滤器,
 
perms,roles,ssl,rest,port是授权过滤器

猜你喜欢

转载自blog.csdn.net/zhou199252/article/details/80741361