文章目录
1.自定义Realm
①创建一个Realm类
- 自定义Realm继承AuthorizingRealm类
- 重写授权和登录验证方法
②重写登录验证方法
- 该方法返回null,表示用户名错误
- 只需将密码交给shiro,shiro可以自动帮你判断密码是否正确
- 需要两个密码,一个传过来的,一个数据库中查询的
- 用户名和密码验证后,需要创建SimpleAuthenticationInfo对象返回
创建SimpleAuthenticationInfo:- 第一个参数:principal(主体) =》登录成功后在任何地方都可以拿到的对象
- 以前开始登录成功我们就把用户放到session中
- 第二个参数:密码(从数据库查出来的密码)
- 第三个参数:盐值salt
- 第四个参数:当前realm的名称(随便取)
//判断密码是否正确
//shiro做准备了一个工具直接让我们把数据变成 ByteSource格式
ByteSource salt = ByteSource.Util.bytes("itsource");
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,salt,getRealmName());
③授权方法重写
- 创建授权对象
- 获取角色并设置角色
- 获取权限并设置权限
/*
* 授权(之后的权限数据获取都在这里面)
* */
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//创建授权对象
SimpleAuthorizationInfo authorizationInfo= new SimpleAuthorizationInfo();
//获取角色并设置角色
Set<String> roles = findRoles();
authorizationInfo.setRoles(roles);
//获取权限并设置权限
Set<String> permissions = findPermissions();
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}
自定义Realm完整代码
public class MyRealm extends AuthorizingRealm {
/*
* 授权(之后的权限数据获取都在这里面)
* */
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//创建授权对象
SimpleAuthorizationInfo authorizationInfo= new SimpleAuthorizationInfo();
//获取角色并设置角色
Set<String> roles = findRoles();
authorizationInfo.setRoles(roles);
//获取权限并设置权限
Set<String> permissions = findPermissions();
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}
/*
* 现在是模拟拿到对应的角色信息(以后我们会根据真实的登录用户去查找对应的角色)
* */
private Set<String> findRoles(){
Set<String> roles = new HashSet<String>();
roles.add("it");
return roles;
}
private Set<String> findPermissions(){
Set<String> permissions = new HashSet<String>();
permissions.add("employee:save");
return permissions;
}
/*
* 登录
* 1.该方法返回null,表示用户名错误
* 2.只需将密码交给shiro,shiro可以自动帮你判断密码是否正确(需要两个密码,一个传过来的,一个数据库中查询到的)
* */
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//拿到用户名密码令牌(强转)
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
//获取用户名(传过来的)
String username = token.getUsername();
//判断用户名是否正确(查询出来的密码)
String password = findByUsername(username);
//返回的密码为null,表示用户名不存在
if (password == null){
return null;
}
//判断密码是否正确
/**
* 第一个参数:principal(主体) =》 登录成功后在任何地方都可以拿到的对象
* 以前开始登录成功我们就把用户放到session中
* 第二个参数:密码(从数据库查出来的密码)
* 第三个参数:盐值salt
* 第四个参数:当前realm的名称(随便取)
*/
//shiro做准备了一个工具直接让我们把数据变成 ByteSource格式
ByteSource salt = ByteSource.Util.bytes("itsource");
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,salt,getRealmName());
return authenticationInfo;
}
private String getRealmName(){
return "myRealm";
}
/*
* 模拟获取密码的方法
* */
private String findByUsername(String username){
if("admin".equals(username)){
return "831d092d59f6e305ebcfa77e05135eac";
}else if("gg".equals(username)){
return "123";
}
return null;
}
}
2.密码加密
- 加密要使用加密算法(散列,哈希)
- 我们主要使用MD5,SHA
- 创建加密对象是传递的参数:
SimpleHash hash = new SimpleHash(“MD5”,“123456”,“itsource”,10);- algorithmName:加密算法名称
- source:密码源
- salt:盐值
- hasIterations:迭代次数
①凭证匹配器(HashedCredentialsMatcher)
通过凭证匹配器,设置加密方式和迭代次数
自定义realm类中
//创建一个凭证匹配器
HashedCredentialsMatcher matcher= new HashedCredentialsMatcher();
//设置加密算法名称
matcher.setHashAlgorithmName("MD5");
//设置迭代次数
matcher.setHashIterations(10);
//设置凭证匹配器(密码验证规则) Credentials(凭证) Matcher(匹配器)
myRealm.setCredentialsMatcher(matcher);
-设置加盐(salt),需要在登录验证方法创建SimpleAuthenticationInfo对象时设置
ByteSource salt = ByteSource.Util.bytes(“itsource”);
//shiro做准备了一个工具直接让我们把数据变成 ByteSource格式
ByteSource salt = ByteSource.Util.bytes("itsource");
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,salt,getRealmName());
测试加密:
/**
* 123456 MD5 =》 e10adc3949ba59abbe56e057f20f883e
* 123456 MD5 10次 =》 4a95737b032e98a50c056c41f2fa9ec6
* 123456 MD5 10次 itsource=》 831d092d59f6e305ebcfa77e05135eac
*/
@Test
public void test() throws Exception{
/*
* algorithmName:加密算法名称
* source:密码源
* salt:盐值
* hasIterations:迭代次数
* */
SimpleHash hash = new SimpleHash("MD5","123456","itsource",10);
System.out.println(hash.toHex());
}
3.测试自定义realm和密码加密
测试代码:
public class MyRealmTest {
@Test
public void testMyRealm() throws Exception{
//获取权限管理对象
DefaultSecurityManager securityManager= new DefaultSecurityManager();
//获取一个(自定义)realm对象
MyRealm myRealm = new MyRealm();
//创建一个凭证匹配器
HashedCredentialsMatcher matcher= new HashedCredentialsMatcher();
//设置加密算法名称
matcher.setHashAlgorithmName("MD5");
//设置迭代次数
matcher.setHashIterations(10);
//设置凭证匹配器(密码验证规则) Credentials(凭证) Matcher(匹配器)
myRealm.setCredentialsMatcher(matcher);
//将realm放入权限管理对象中
securityManager.setRealm(myRealm);
//将权限管理器放入工具
SecurityUtils.setSecurityManager(securityManager);
//获取当前用户
Subject subject = SecurityUtils.getSubject();
//判断用户是否登录
System.out.println("判断用户是否登录"+subject.isAuthenticated());
if(!subject.isAuthenticated()){
try {
//创建一个用户名密码令牌
UsernamePasswordToken token = new UsernamePasswordToken("admin","123456");
//通过令牌,登录当前用户
subject.login(token);
//再次判断用户是否登录
System.out.println("再次判断用户是否登录"+subject.isAuthenticated());
} catch (UnknownAccountException e) {
//UnknownAccountException(不知道用户异常)
System.out.println("用户名错误!!!");
e.printStackTrace();
}catch (IncorrectCredentialsException e){
//IncorrectCredentialsException(不正确凭证(密码错误)异常)
System.out.println("密码错误!!!");
e.printStackTrace();
}catch (AuthenticationException e){
System.out.println("sorry!!!");
e.printStackTrace();
}
}
//判断该用户是否有某一个角色
System.out.println(subject.hasRole("admin"));
//判断是否有某个权限
System.out.println("判断用户是否有employee:save权限"+subject.isPermitted("employee:save"));
System.out.println("判断用户是否有employee:update权限"+subject.isPermitted("employee:update"));
System.out.println("判断用户是否有employee:delete权限"+subject.isPermitted("employee:delete"));
System.out.println("判断用户是否有department:save权限"+subject.isPermitted("department:save"));
}
}
4.Spring集成shiro
①导包
- 导入shiro核心包
<!-- shiro(权限框架)的支持包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.4.0</version>
<type>pom</type>
</dependency>
<!-- shiro与Spring的集成包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
②web.xml配置代理过滤器
- 该过滤器除了拦截,啥也不做
<!--
shiro过滤器(除了拦截,啥也不做)
Delegating: 授(权); 把(工作、权力等)委托(给下级); 选派(某人做某事);
Proxy:代理
-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
③创建并配置applicationContext-shiro.xml
注意applicationContext.xml需要引入applicationContext-shiro.xml
applicationContext.xml代码
<!--引入applicationContext-shiro.xml文件-->
<import resource="classpath:applicationContext-shiro.xml"/>
applicationContext-shiro.xml代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!--
创建权限管理器对象
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(aisellRealm);
-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="aisellRealm"/>
</bean>
<!--
创建realm(获取权限数据的对象)
-->
<bean id="aisellRealm" class="com.luo.aisell.web.shiro.AisellRealm">
<property name="name" value="aisellRealm"/>
<!--
创建 凭证credentials匹配器Matcher
aisellRealm.setCredentialsMatcher(matcher);
-->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!--加密算法-->
<property name="hashAlgorithmName" value="MD5"/>
<!--迭代次数-->
<property name="hashIterations" value="10"/>
</bean>
</property>
</bean>
<!--建议大家留住它:可以支持注解权限控制-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- shiro真正的权限过滤器 注意:这个id必须和shiro过滤器的名称相同-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 引入了权限管理器 -->
<property name="securityManager" ref="securityManager"/>
<!-- 如果没有登录成功,就会进入这个页面 -->
<property name="loginUrl" value="/s/login.jsp"/>
<!--登录成功后,会进入的页面-->
<property name="successUrl" value="/s/success.jsp"/>
<!--如果没有权限,你会进入这个页面-->
<property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>
<!--
filterChainDefinitions:过滤器描述(注意描述是有顺序的)
anon:不登录也可以访问的(游客也可以访问)
/s/permission.jsp = perms[employee:index]
只有employee:index权限,才能访问/s/permission.jsp
authc:必须登录才可以访问的
-->
<property name="filterChainDefinitions">
<value>
/login = anon
/s/permission.jsp = perms[employee:index]
/** = authc
</value>
</property>
</bean>
</beans>
④准备工厂返回权限信息
- 返回的Map值是有顺序的
- 修改后要重启(热启动无效)
这个类就是咱们获取权限Map的工厂
//这个类就是咱们获取权限Map的工厂
public class FilterChainDefinitionMapFactory {
/**
* 这个方法就会返回咱们的权限数据(它是有顺序)
*/
public Map<String,String> creatMap(){
//注意:这里的map是有顺序的
Map<String,String> map = new LinkedHashMap<>();
map.put("/login","anon");
map.put("/s/permission.jsp","perms[employee:save]");
map.put("/**","authc");
return map;
}
}
applicationContext-shiro.xml代码
<!-- shiro真正的权限过滤器 注意:这个id必须和shiro过滤器的名称相同-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 引入了权限管理器 -->
<property name="securityManager" ref="securityManager"/>
<!-- 如果没有登录成功,就会进入这个页面 -->
<property name="loginUrl" value="/s/login.jsp"/>
<!--登录成功后,会进入的页面-->
<property name="successUrl" value="/s/success.jsp"/>
<!--如果没有权限,你会进入这个页面-->
<property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>
<!--
filterChainDefinitions:过滤器描述(注意描述是有顺序的)
anon:不登录也可以访问的(游客也可以访问)
authc:必须登录才可以访问的
-->
<!--引用map-->
<property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
</bean>
<!--通过工厂方法获得获得一个map对象 将它变成一个bean-->
<bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapFactory" factory-method="creatMap"/>
<!--创建map工厂-->
<bean id="filterChainDefinitionMapFactory" class="com.luo.aisell.web.shiro.FilterChainDefinitionMapFactory"/>