Shiro框架
1.基本流程
1)依赖项
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-cas</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.2</version>
</dependency>
2)加密包 org.apache.shiro.crypto
//盐
RandomNumberGenerator generator = new SecureRandomNumberGenerator();
String salt = generator.nextBytes().toHex();
//密码算法
String algorithmName = "md5";
//加密原码
Object source ="123456";
//盐
Object salt = generator.nextBytes().toHex();
//重复加密次数
int hashIterations =1;
SimpleHash simpleHash = new SimpleHash(algorithmName, source, salt, hashIterations);
3)XML配置
spring-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 ">
<!-- 加密 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="md5"></property>
<property name="hashIterations" value="1"></property>
</bean>
<!-- 3end 业务-->
<bean id="userRealm" class="com.uplooking.shiro.UserRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"></property>
</bean>
<!-- 2↑ 权限管理 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm"></property>
</bean>
<!-- 1.1↑ 权限拦截 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- b end CAS认证过滤器 -->
<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
<property name="failureUrl" value="/user/login"/>
</bean>
<!-- 1.2↑ 过滤规则 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/user/login"/>
<property name="successUrl" value="/user/login"/>
<property name="filters">
<map>
<!-- a↑ 过滤规则 -->
<entry key="cas" value-ref="casFilter"/>
</map>
</property>
<property name="filterChainDefinitions">
<value>
/user/logout=logout
/user/**=anon
/static/**=anon
/authority/**=authc
</value>
</property>
</bean>
<!--开启shiro的注解-->
<bean id="advisorAutoProxyCreator"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<property name="proxyTargetClass" value="true"></property>
</bean>
<!-- Shiro生命周期处理器 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
</beans>
web.xml
<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>
4)注册(密码加密)
正常业务流程 (Shiro框架加密工具)
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@Transactional(propagation=Propagation.REQUIRED)
public Map<String, Object> saveUser(String path, UserVO record, MultipartFile file) throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
UserVOExample example = new UserVOExample();
//用户名唯一
example.createCriteria().andUnameEqualTo(record.getUname());
if(userMapper.countByExample(example)==0){
example.clear();
//联系电话唯一
example.createCriteria().andUphoneEqualTo(record.getUphone());
if(userMapper.countByExample(example)==0){
//随机盐值
String salt = new SecureRandomNumberGenerator().nextBytes().toHex();
record.setUsalt(salt);
//密码加密
String pwd = new SimpleHash(ShiroConsts.ALGORITHMNAME, UserConsts.DEFAULTPWD, salt,
ShiroConsts.HASHITERATIONS).toHex();//md5,123456,随机盐值,1
record.setUpwd(pwd);
//默认积分
record.setUfen(UserConsts.DEFAULTFEN);//0
//默认状态
record.setUstatus(UserConsts.DEFAULTUSATUS);//正常
//后缀名
//默认图片
byte[] data = null;
//为空使用系统(服务器上保存的)默认图片作为头像
if(file==null || file.isEmpty()){
//获取图片的后缀,DEAFULTPHOTOPATH:系统默认图片的path
record.setUphoto(FileUtils.getSuffix(UserConsts.DEAFULTPHOTOPATH));
//复制一份系统默认图片
data = FileUtils.readFile(FileUtils.parentPath(path)+UserConsts.DEAFULTPHOTOPATH);
}else{
//获取用户上传的图片后缀
record.setUphoto(FileUtils.getSuffix(file.getOriginalFilename()));
data = file.getBytes();
}
if(userMapper.insert(record)==1){
//图片上传
path = FileUtils.parentPath(path)+UserConsts.PHOTOUPLOADPATH+record.getUid()+record.getUphoto();
FileUtils.writeFile(path, data);
map.put(ConfigConsts.CODE, ConfigConsts.SUCCESSCODE);
map.put(ConfigConsts.MESSAGE, UserConsts.SAVEUSERSUSCCESSMESSGE);
}else{
map.put(ConfigConsts.CODE, UserConsts.SAVEUSERFAILCODE);
map.put(ConfigConsts.MESSAGE, UserConsts.SAVEUSERFAILMESSAGE);
}
}else{
map.put(ConfigConsts.CODE, UserConsts.USERPHONEEXISTCODE);
map.put(ConfigConsts.MESSAGE, UserConsts.USERPHONEEXISTMESSAGE);
}
}else{
map.put(ConfigConsts.CODE, UserConsts.USERNAMEEXISTCODE);
map.put(ConfigConsts.MESSAGE, UserConsts.USERNAMEEXISTMESSAGE);
}
return map;
}
}
登录
5)认证与授权
package com.uplooking.shiro;
//shiro
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserMapper userMapper;
@Autowired
private UserRoleMapper userRoleMapper;
@Autowired
private RoleMapper roleMapper;
//授权(角色路由授权)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取当前发起路由的对象信息
Object primaryPrincipal = principalCollection.getPrimaryPrincipal();
System.out.println(primaryPrincipal);
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
if(primaryPrincipal instanceof UserVO){
UserVO userVO=(UserVO) primaryPrincipal;
List<String> roles = userVO.getRoles();
//为当前用户进行角色授权
info.addRoles(roles);
}
return info;
}
//认证(密码认证)
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
//令牌 用户对象的载体->获取用户在认证时提交的用户名和(明文)密码
String name=authenticationToken.getPrincipal().toString();
//根据用户名获取用户对象,在进行认证
UserVO userVO = userMapper.selectByUserName(name);
if(userVO==null){
throw new UnknownAccountException(ShiroConsts.UNKNOWACCOUNT);//用户名不存在
}
if(userVO.getUstatus()== UserConsts.LOCKUSATUS){//用户状态为103
throw new LockedAccountException(ShiroConsts.ACCOUNTLOCK);//用户被冻结
}
//认证后存储登陆信息
Object principal=userVO;
//绑定当前用户的拥有角色
List<String> unamelist=new ArrayList<>();
List<String> unamezhlist=new ArrayList<>();
//通过多表查询关联用户和角色
UserRoleVOExample userRoleVOExample=new UserRoleVOExample();
userRoleVOExample.createCriteria().andUruidEqualTo(userVO.getUid());
List<UserRoleVO> userRoleVOS = userRoleMapper.selectByExample(userRoleVOExample);
for (UserRoleVO userRoleVO : userRoleVOS) {
RoleVO roleVO = roleMapper.selectByPrimaryKey(userRoleVO.getUrrid());
unamelist.add(roleVO.getRname());
unamezhlist.add(roleVO.getRnamezh());
}
userVO.setRoles(unamelist);
userVO.setRoleZHs(unamezhlist);
//获得加密后的密码
Object hashed=userVO.getUpwd();
//获得用户当前用户的盐值
ByteSource salt=ByteSource.Util.bytes(userVO.getUsalt());
//名称
String realmName=this.getName();
//认证
return new SimpleAuthenticationInfo(principal,hashed,salt,realmName);
}
}
控制层:
@RequestMapping(value="/user/login",method=RequestMethod.POST)
public String method21(String name,String pwd,@RequestParam(name="rememberMe",defaultValue="false")Boolean rememberMe) throws Exception{
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(name, pwd,rememberMe);
subject.login(token);//回调UserRealm类中的认证业务 认证出错抛异常
return "redirect:/index.jsp";
}
认证抛出异常处理类AdviceController.java
@ControllerAdvice
public class AdviceController {
@ExceptionHandler(Exception.class)
public String method(Exception ex){
if (ex instanceof UnknownAccountException){
return "redirect:/static/error/error-301.jsp";//用户不存在
}
if (ex instanceof LockedAccountException){
return "redirect:/static/error/error-302.jsp";//用户名或密码错误
}
if (ex instanceof AuthorizationException){
return "redirect:/static/error/error-303.jsp";//权限不足
}
return "redirect:/static/error/error-500.jsp";//错误
}
}
配置解析
//过滤规则
/*
authc -> 认证后才能访问
user -> 记住我(Cookie)后可以访问
anon -> 匿名可以访问
logout -> 注销
*/
<property name="filterChainDefinitions">
<value>
/emp=user
/user/logout=logout
/user/**=anon
/static/**=anon
</value>
</property>
标签
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<shiro:authenticated> -> 成功认证后
<shiro:notAuthenticated> -> 未认证前
<shiro:user> -> 记住我之后 (用户对象序列化)
<shiro:principal> -> 当前认证用户信息(默认:用户名)
6)授权 (authorization) Controller
1>标签 (治标不治本) ->盗链(路由)
2>配置 (可读性弱)
/dept/**=authc,roles[deptManager]
/safe/list=authc,perms[sale:list]
3>注解 (推荐)
@RequiresRoles(value={"safeManager","admin"},logical=Logical.OR)//两种注释用其一
@RequiresPermissions(value={"safe:list","safe:get"},logical=Logical.OR)//两种注释用其一
@RequestMapping(value="/safe/list",method=RequestMethod.GET)
public String method1() throws Exception{
System.out.println("安全查询");
return "safe/info";
}
2.工作流程
1)登录时 回调认证业务
2)访问时 回调授权业务