- shiro入门
Shiro权限管理
Apache Shiro是一个强大且易用的Java安全框架,有身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
Spring security 重量级安全框架
Apache Shiro轻量级安全框架
- 一、自定义Realm
导包
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
MyRealm
package com.xpc.realm;
import org.apache.commons.collections.SynchronizedPriorityQueue;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import java.util.HashSet;
import java.util.Set;
//自定义Realm,需要去实现一个Realm接口
public class MyRealm extends AuthorizingRealm {
//设置一个名字
@Override
public String getName(){
return "MyRealm";
}
//权限认证
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//拿到当前登陆的用户
String username = (String) principalCollection.getPrimaryPrincipal();
//通过模拟数据库的方法拿到角色与权限
Set<String> roles = getRoles(username);
Set<String> permissions = getPermissions(username);
//创建返回权限对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//把角色和权限设置到返回对象中去
info.setRoles(roles);
info.setStringPermissions(permissions);
return info;
}
//根据用户名拿到角色
public Set<String> getRoles(String sername){
Set<String> roles = new HashSet<String>();
roles.add("admin");
roles.add("it");
return roles;
}
//根据用户名拿到权限
public Set<String> getPermissions(String sername){
Set<String> perms = new HashSet<String>();
perms.add("employee:*");
//perms.add("employee:update");
return perms;
}
//登陆认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//拿到令牌
UsernamePasswordToken token =(UsernamePasswordToken)authenticationToken;
String username = token.getUsername();//拿到用户名
String password = getByName(username);
if (password==null)
return null; //表示该用户不存在
/**
* Object principal:用户名
* Object credentials:密码
* String realmName:取名(随便取)
*/
//获取到盐值
ByteSource salt = ByteSource.Util.bytes("xpc");
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, salt,getName());
return simpleAuthenticationInfo;
}
//模拟从数据库拿数据的过程
public String getByName(String username){
if ("admin".equals(username)){
return "c5e48d40051cc49bd56cbfe49346b23c"; //123456
}else if("xpc".equals(username)){
return "ec8cc66a7f7457a45e4dab44d0882d78"; //334455
}
//如果没有该用户就返回null
return null;
}
}
- 功能测试MyRealmTest
package com.xpc.realm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
public class MyRealmTest {
@Test
public void testMyRealm(){
//新建一个自己的Realm
MyRealm myRealm = new MyRealm();
//HashedCredentialsMatcher:hashed密码匹配器
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
//设置加密算法
matcher.setHashAlgorithmName("MD5");
//设置加密次数
matcher.setHashIterations(10);
//匹配器交给Realm
myRealm.setCredentialsMatcher(matcher);
//根据Realm创建SecurityManager对象
DefaultSecurityManager securityManager = new DefaultSecurityManager(myRealm);
//把它变成一个公共的对象
SecurityUtils.setSecurityManager(securityManager);
//拿到主体
Subject currentUser = SecurityUtils.getSubject();
//准备登陆
if(!currentUser.isAuthenticated()){
try {
//准备令牌
UsernamePasswordToken token = new UsernamePasswordToken("xpc","334455");
//登陆
currentUser.login(token);
System.out.println("查看是否登陆成功:"+currentUser.isAuthenticated());
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("对不起,您输入对的账号有误!");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("对不起,您输入对的密码有误!");
}catch (AuthenticationException e){
e.printStackTrace();
System.out.println("发生了一个未知的错误");
}
}
System.out.println("判断是否是admin角色"+currentUser.hasRole("admin"));
System.out.println("判断是否有employee:*这个权限"+currentUser.isPermitted("employee:*"));
}
@Test
public void testPassword(){
/*
String algorithmName("MD5"):加密算法
Object source("admin"):密码
Object salt("xpc"):加的盐值
int hashIterations(10):加密次数
*/
SimpleHash hash = new SimpleHash("MD5","334455","xpc",10);
System.out.println(hash.toHex());
}
}
- 二、Shiro集成Spring
web.xml配置
<!--shiro的配置-->
<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.xml配置
<import resource="classpath:applicationContext-shiro.xml" />
在main/resources里创建一个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" xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd">
<!--DefaultWebSecurityManager:创建Shiro的核心对象
相当于: SecurityManager sm = new DefaultWebSecurityManager(myRealm);-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="jdbcRealm"/>
</bean>
<!-- 自己的Realm -->
<bean id="jdbcRealm" class="com.xpc.web.shior.MyrRealm">
<!--密码的匹配器-->
<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>
<!-- Secure Spring remoting: Ensure any Spring Remoting method invocations can be associated
with a Subject for security checks. -->
<bean id="secureRemoteInvocationExecutor" class="org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- 这个是真正的shiro过滤器
要求:这个id的名称必需和web.xml中的那个过滤器名称一致 -->
<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/main.jsp"/>
<!-- 没有权限进入的页面 -->
<property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>
<!--权限路径设置
路径 = anon -> 没有登录也可以访问这个路径
路径 = perms[user:index] -> 该路径需要相应的权限才可以进入
/** = authc -> 所有路径都要权限拦截
-->
<!--<property name="filterChainDefinitions">-->
<!--<value>-->
<!--/login = anon-->
<!--/employee/index = perms[employee:index]-->
<!--/dept/index = perms[dept:index]-->
<!--/** = authc-->
<!--</value>-->
<!--</property>-->
<!--权限路径设置 通过Map的方式-->
<property name="filterChainDefinitionMap" ref="filterChainDefinitionMap">
</property>
</bean>
<!--实体工厂bean,把它返回值也变成一个bean-->
<bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapFactory" factory-method="builderFilterChainDefinitionMap" />
<!-- 拿到生成map的工厂 -->
<bean id="filterChainDefinitionMapFactory" class="com.xpc.web.shior.FilterChainDefinitionMapFactory"/>
</beans>
Java代码
MyrRealm
package com.xpc.web.shior;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import java.util.HashSet;
import java.util.Set;
public class MyrRealm extends AuthorizingRealm {
@Override
public String getName(){
return "MyrRealm";
}
//权限认证
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = (String) principalCollection.getPrimaryPrincipal();
Set<String> roles = getRoles(username);
Set<String> permissions = getPermissions(username);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
}
//根据用户名拿到角色
public Set<String> getRoles(String sername){
Set<String> roles = new HashSet<String>();
roles.add("admin");
roles.add("it");
return roles;
}
//根据用户名拿到权限
public Set<String> getPermissions(String sername){
Set<String> perms = new HashSet<String>();
perms.add("employee:*");
perms.add("dept:*");
return perms;
}
//登录认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
String username = token.getUsername();
String password = getPassword(username);
if (password==null)
return null;
ByteSource salt = ByteSource.Util.bytes("xpc");//获取到盐值
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, salt, getName());
return simpleAuthenticationInfo;
}
//模拟从数据库拿到密码的过程
public String getPassword(String username){
if ("admin".equals(username)){
return "c5e48d40051cc49bd56cbfe49346b23c"; //123456
}else if ("xpc".equals(username)){
return "ec8cc66a7f7457a45e4dab44d0882d78"; //334455
}
return null; //表示没有该用户
}
}
FilterChainDefinitionMapFactory
package com.xpc.web.shior;
import org.apache.commons.collections.map.LinkedMap;
import java.util.LinkedHashMap;
import java.util.Map;
//用类的方式来做权限拦截
/*
/login = anon
/employee/index = perms[employee:index]
/dept/index = perms[dept:index]
/** = authc
*/
public class FilterChainDefinitionMapFactory {
public Map<String,String> builderFilterChainDefinitionMap(){
//LinkedHashMap是有序的
Map<String,String> map = new LinkedHashMap();
//设置放行
map.put("/login", "anon");
设置权限拦截
map.put("/employee/index", "perms[employee:index]");
map.put("/dept/index", "perms[dept:index]");
//设置所有拦截
map.put("/**", "authc");
return map;
}
}
LoginController
package com.xpc.web.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(String username,String password){
//1.拿到subject(当前用户)
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated()){
try{
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
subject.login(token);
System.out.println("查看是否登陆成功:"+subject.isAuthenticated());
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("对不起,您输入对的账号有误!");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("对不起,您输入对的密码有误!");
}catch (AuthenticationException e){
e.printStackTrace();
System.out.println("发生了一个未知的错误");
}
}
//登陆成功就进入成功页面,没登陆成功就再次登陆
if (subject.isAuthenticated()){
return "redirect:/s/main.jsp";
}else {
return "forward:/s/login.jsp";
}
}
}