#Shiro
shiro主要是对用户登录、用户角色以及用户对应的权限进行认证管理
本文主要分为单个realm和多个realm进行认证授权管理测试demo
直接上代码多的也没有意义,我这边是结合springboot为例的
##单个realm
代码格式布局
###pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>shiro_demo</artifactId>
<groupId>com.lix</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shiro_springboot</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.5.1</version>
</dependency>
</dependencies>
</project>
###property文件
#shiro
shiro.loginUrl=/myController/login
# properties
server.port=8081
spring.thymeleaf.cache=false
spring.datasource.name=shirodb
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root
# mybatis config
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
###realm
这个文件主要是根据账号查询出用户的身份信息、角色以及权限信息封装返回由底层进行比较
package com.lix.shiro.realm;
import com.lix.shiro.entity.User;
import com.lix.shiro.entity.UserRole;
import com.lix.shiro.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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 org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
@Slf4j
@Component
public class MyRealm extends AuthorizingRealm {
@Resource
private UserService userService;
// 自定义授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.info("MyRealm-固定用户角色权限测试");
log.info("自定义授权方法-查询用户的角色以及对应拥有的权限");
String username = principalCollection.getPrimaryPrincipal().toString();
// 查询用户角色
List<UserRole> userRole = userService.getUserRole(username);
HashSet<String> set = new HashSet<>();
for(UserRole userRoleInfo:userRole){
String roleName = userRoleInfo.getRoleName();
set.add(roleName);
}
log.info("用户["+username+"]拥有的角色为:"+set);
// 查询用户权限
List<String> userPermissions = userService.getUserPermission(username);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(set);
info.addStringPermissions(userPermissions);
return info;
}
// 自定义登录认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.info("MyRealm--验证身份信息");
log.info("自定义登录认证方法--查询用户的身份信息");
// 获取用户身份信息
String username = authenticationToken.getPrincipal().toString();
String credentials = new String((char[])authenticationToken.getCredentials());
// 获取持久化的用户信息
User user = userService.getUserInfoByName(username);
// 身份信息判断
if(user==null){
log.info("系统不存在该用户");
return null;
}
// 封装身份信息返回认证
AuthenticationInfo info = new SimpleAuthenticationInfo(
username,
user.getPassword(),
ByteSource.Util.bytes(username),
authenticationToken.getPrincipal().toString()
);
log.info("系统用户认证信息:"+username+"==="+credentials);
return info;
}
}
###shiroConfig
这个类主要功能就是shiro对请求进行拦截,规定需要通过身份认证才可以访问对应的页面或者具有相对的功能,以及还规定了认证需要的策略、加密方式、加密次数
package com.lix.shiro.config;
import com.lix.shiro.realm.MyRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AllSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class ShiroConfig {
@Resource
private MyRealm myRealm;
/**
* 配置SecurityManager
*
* 认证策略
* AtLeastOneSuccessfulStrategy--一个或多个Realm验证成功即可(默认认证策略)
* FirstSuccessfulStrategy--第一个Realm验证成功 后续Realm忽略 视为成功
* AllSuccessfulStrategy--所有Realm成功 认证视为成功
*/
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
// 1.创建defaultWebSecurityManager对象
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 2.创建加密对象,设置相关属性
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");
//matcher.setHashSalted(true);
// matcher.setHashIterations(3);
// 3.将加密对戏存储到myRealm中
myRealm.setCredentialsMatcher(matcher);
// 4.将myRealm存入创建defaultWebSecurityManager对象
defaultWebSecurityManager.setRealm(myRealm);
// 5.设置rememberme
defaultWebSecurityManager.setRememberMeManager(rememberMeManager());
// 6.返回
return defaultWebSecurityManager;
}
// 配置shiro内置过滤器拦截范围
@Bean
public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
// 设置不认证可以访问的资源
definition.addPathDefinition("/myController/userLogin","anon");
definition.addPathDefinition("/myController/login","anon");
// 设置登出过滤器
definition.addPathDefinition("/logout","logout");
// 设置需要登录认证的拦截资源
definition.addPathDefinition("/**","authc");
definition.addPathDefinition("/**","user");
return definition;
}
// 创建shiro的cookie管理对象
private RememberMeManager rememberMeManager() {
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
return cookieRememberMeManager;
}
// cookie属性设置
private Cookie rememberMeCookie() {
SimpleCookie cookie = new SimpleCookie("rememberMe");
// 设置跨域
//cookie.setDomain(domain);
cookie.setPath("/");
cookie.setHttpOnly(true);
cookie.setMaxAge(30*24*60*60);
return cookie;
}
}
###controller
package com.lix.shiro.controller;
import com.lix.shiro.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("myController")
@Slf4j
public class UserController {
@GetMapping("login")
public String login() {
return "login";
}
@GetMapping("userLogin")
public String userLogin(User user, HttpSession session,
@RequestParam(defaultValue = "false")Boolean rememberMe){
log.info("是否记住密码="+rememberMe);
// 1.获取subject对象
Subject subject = SecurityUtils.getSubject();
// 2.封装数据到token
AuthenticationToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword(),rememberMe);
// 3.调用login方法进行认证
try {
subject.login(token);
session.setAttribute("user",token.getPrincipal().toString());
log.info("登录成功!");
}catch (AuthenticationException e){
log.info("登录失败!");
e.printStackTrace();
}
return "main";
}
@GetMapping("userLoginRm")
public String userLoginRm(HttpSession session){
Subject subject = SecurityUtils.getSubject();
String s = subject.getPrincipal().toString();
log.info("===记住账号密码验证测试===");
log.info("subject账号信息:"+s);
return "main";
}
@RequiresRoles("role:admin")
@GetMapping("userLoginRole")
@ResponseBody
public String userLoginRole(){
log.info("测试角色认证成功");
return "测试角色认证成功";
}
@RequiresPermissions("user:add")
@GetMapping("userLoginPermissions")
@ResponseBody
public String userLoginPermissions(){
log.info("测试权限认证成功");
return "测试权限认证成功";
}
}
###service
package com.lix.shiro.service;
import com.lix.shiro.entity.User;
import com.lix.shiro.entity.UserRole;
import java.util.List;
public interface UserService {
// 用户登录
User getUserInfoByName(String username);
// 角色查询
List<UserRole> getUserRole(String username);
// 权限查询
List<String> getUserPermission(String username);
}
###serviceImpl
package com.lix.shiro.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lix.shiro.entity.User;
import com.lix.shiro.entity.UserRole;
import com.lix.shiro.mapper.UserMapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class UserServiceImpl implements UserService{
@Resource
private UserMapper userMapper;
@Override
public User getUserInfoByName(String username) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",username);
User user = userMapper.selectOne(queryWrapper);
return user;
}
@Override
public List<UserRole> getUserRole(String username) {
List<UserRole> list = userMapper.rolesQryByName(username);
return list;
}
@Override
public List<String> getUserPermission(String username) {
List<String> strings = userMapper.permissionQryByName(username);
return strings;
}
}
###dao
package com.lix.shiro.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lix.shiro.entity.User;
import com.lix.shiro.entity.UserRole;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface UserMapper extends BaseMapper<User> {
public List<UserRole> rolesQryByName(String name);
public List<String> permissionQryByName(String name);
}
###mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lix.shiro.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.lix.shiro.entity.UserRole">
<id column="id" jdbcType="BIGINT" property="Id"/>
<result column="userName" jdbcType="VARCHAR" property="userName"/>
<result column="roleName" jdbcType="VARCHAR" property="roleName"/>
</resultMap>
<select id="rolesQryByName" parameterType="java.lang.String" resultMap="BaseResultMap">
select
distinct a.id id ,a.username userName,c.name rolename
from sys_user a
left join sys_user_role b on a.id=b.user_id
left join sys_role c on b.role_id=c.id
where a.username = #{username}
</select>
<select id="permissionQryByName" parameterType="java.lang.String" resultType="java.lang.String">
select
distinct f.permission permission
from sys_user a
left join sys_user_role b on a.id=b.user_id
left join sys_role c on b.role_id=c.id
left join sys_role_resources d on c.id=d.role_id
left join sys_resources f on d.resources_id=f.id
where a.username = #{username} and f.permission is not null and f.permission !=''
</select>
</mapper>
###entity实体类
package com.lix.shiro.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("sys_user")
public class User {
private Integer id;
private String username;
private String password;
}
package com.lix.shiro.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserRole {
private Integer Id;
private String userName;
private String roleName;
}
###html
login.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en" xml:th="http://www.thymeleaf.org" >
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Shiro登录认证页面</h1>
<br>
<form action="/myController/userLogin">
<div>用户名:<input type="text" name="username" value=""></div>
<div>密码:<input type="password" name="password" value=""></div>
<div>记住用户:<input type="checkbox" name="rememberMe" value="true"></div>
<div><input type="submit" value="登录"></div>
</form>
</body>
</html>
main.html
<!DOCTYPE html>
<html lang="en" xml:th="http://www.thymeleaf.org" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Shiro登录认证成功后主页面</h1>
<br>
登录用户名为:<span th:text="${session.user}"></span>
<br>
<a href="/logout">登出</a>
<br>
<a href="/myController/userLoginRole">测试-角色</a>
<br>
<a href="/myController/userLoginPermissions">测试-权限</a>
</body>
</html>
##多个realms
代码结构
只添加改动或者新增的地方
###多个realms
package com.lix.shiro.realm;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class EmailRealm extends AuthorizingRealm {
// 自定义授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
// 自定义登录认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.info("EmailRealm--验证身份信息");
// 获取用户身份信息
String email = authenticationToken.getPrincipal().toString();
String password = new String((char[])authenticationToken.getCredentials());
// 根据邮箱获取用户信息-模拟就是用户前台输入的邮箱密码
log.info("EmailRealm:"+email+"==="+password);
// 封装身份信息返回认证
AuthenticationInfo info = new SimpleAuthenticationInfo(
email,
password,
authenticationToken.getPrincipal().toString()
);
return info;
}
}
package com.lix.shiro.realm;
import com.lix.shiro.entity.User;
import com.lix.shiro.entity.UserRole;
import com.lix.shiro.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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 org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
@Slf4j
@Component
public class NameRealm extends AuthorizingRealm {
@Resource
private UserService userService;
// 自定义授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.info("MyRealm-固定用户角色权限测试");
log.info("自定义授权方法-查询用户的角色以及对应拥有的权限");
String username = principalCollection.getPrimaryPrincipal().toString();
// 查询用户角色
List<UserRole> userRole = userService.getUserRole(username);
HashSet<String> set = new HashSet<>();
for(UserRole userRoleInfo:userRole){
String roleName = userRoleInfo.getRoleName();
set.add(roleName);
}
log.info("用户["+username+"]拥有的角色为:"+set);
// 查询用户权限
List<String> userPermissions = userService.getUserPermission(username);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(set);
info.addStringPermissions(userPermissions);
return info;
}
// 自定义登录认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.info("MyRealm--验证身份信息");
log.info("自定义登录认证方法--查询用户的身份信息");
// 获取用户身份信息
String username = authenticationToken.getPrincipal().toString();
String credentials = new String((char[])authenticationToken.getCredentials());
// 获取持久化的用户信息
User user = userService.getUserInfoByName(username);
// 身份信息判断
if(user==null){
log.info("系统不存在该用户");
return null;
}
// 封装身份信息返回认证
AuthenticationInfo info = new SimpleAuthenticationInfo(
username,
user.getPassword(),
ByteSource.Util.bytes(username),
authenticationToken.getPrincipal().toString()
);
log.info("系统用户认证信息:"+username+"==="+credentials);
return info;
}
}
package com.lix.shiro.realm;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class PhoneRealm extends AuthorizingRealm {
// 自定义授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
// 自定义登录认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.info("PhoneRealm--验证身份信息");
// 获取用户身份信息
String phone = authenticationToken.getPrincipal().toString();
String password = new String((char[])authenticationToken.getCredentials());
// 根据手机号获取用户信息-模拟就是用户前台输入的手机号密码
log.info("PhoneRealm系统用户认证信息:"+phone+"==="+password);
// 封装身份信息返回认证
AuthenticationInfo info = new SimpleAuthenticationInfo(
phone,
password,
authenticationToken.getPrincipal().toString()
);
return info;
}
}
###config
这个方法是重写的 主要就是告诉shiro需要调用哪些realm
package com.lix.shiro.config;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.pam.AuthenticationStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 自定义身份认证realm控制器
*
* @ClassName: MyModularRealmAuthenticator
* @Description 用于告诉shiro使用哪个realm处理
*/
public class MyModularRealmAuthenticator extends ModularRealmAuthenticator {
private static final Logger log = LoggerFactory.getLogger(ModularRealmAuthenticator.class);
@Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
throws AuthenticationException {
// 判断getRealms()是否返回为空
assertRealmsConfigured();
// 强制转换回自定义的CustomizedToken
MyUsernamePasswordToken userToken = (MyUsernamePasswordToken) authenticationToken;
// 登录类型
String loginType = userToken.getLoginType();
// 所有Realm
Collection<Realm> realms = getRealms();
// 登录类型对应的所有Realm
Collection<Realm> typeRealms = new ArrayList<>();
for (Realm realm : realms) {
if (realm.getName().contains(loginType)) {
typeRealms.add(realm);
}
}
// 判断是单Realm还是多Realm
if (typeRealms.size() == 1) {
//单realm处理
return doSingleRealmAuthentication(((ArrayList<Realm>) typeRealms).get(0), userToken);
} else {
//多realm处理,需满足全部realm认证
return doMultiRealmAuthentication(typeRealms, userToken);
}
}
/**
* 重写doMultiRealmAuthentication,修复多realm联合认证只出现AuthenticationException异常,而未处理其他异常
*/
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) throws AuthenticationException {
AuthenticationStrategy strategy = getAuthenticationStrategy();
AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);
if (log.isTraceEnabled()) {
log.trace("Iterating through {} realms for PAM authentication", realms.size());
}
for (Realm realm : realms) {
aggregate = strategy.beforeAttempt(realm, token, aggregate);
if (realm.supports(token)) {
log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);
AuthenticationInfo info = null;
Throwable t = null;
try {
info = realm.getAuthenticationInfo(token);
} catch (Throwable throwable) {
t = throwable;
if (log.isDebugEnabled()) {
String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:";
log.debug(msg, t);
}
}
//处理只提示AuthenticationException异常问题
if(t instanceof AuthenticationException) {
log.debug("realmName:"+realm.getName(), t);
throw ((AuthenticationException)t);
}
aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);
} else {
log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token);
}
}
aggregate = strategy.afterAllAttempts(token, aggregate);
return aggregate;
}
}
package com.lix.shiro.config;
import org.apache.shiro.authc.UsernamePasswordToken;
/**
*
* @ClassName: MyUsernamePasswordToken
* @Description 自定义用户名密码凭证类,携带登录类型信息
* @version
*
*/
public class MyUsernamePasswordToken extends UsernamePasswordToken {
private static final long serialVersionUID = 1L;
//登录类型
private String loginType;
public MyUsernamePasswordToken(String username, final String password, String loginType) {
super(username, password);
this.loginType = loginType;
}
public String getLoginType() {
return loginType;
}
public void setLoginType(String loginType) {
this.loginType = loginType;
}
}
package com.lix.shiro.config;
import com.lix.shiro.realm.EmailRealm;
import com.lix.shiro.realm.NameRealm;
import com.lix.shiro.realm.PhoneRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AllSuccessfulStrategy;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class ShiroConfig {
@Resource
private NameRealm nameRealm;
@Resource
private PhoneRealm poneRealm;
@Resource
private EmailRealm emailRealm;
/**
* 配置SecurityManager
*
* 认证策略
* AtLeastOneSuccessfulStrategy--一个或多个Realm验证成功即可(默认认证策略)
* FirstSuccessfulStrategy--第一个Realm验证成功 后续Realm忽略 视为成功
* AllSuccessfulStrategy--所有Realm成功 认证视为成功
*/
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
// 1.创建defaultWebSecurityManager对象
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 2.创建加密对象,设置相关属性
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");
//matcher.setHashSalted(true);
// matcher.setHashIterations(3);
// 3.将加密对戏存储到nameRealm中
nameRealm.setCredentialsMatcher(matcher);
// 4.将nameRealm存入创建defaultWebSecurityManager对象
defaultWebSecurityManager.setRealm(nameRealm);
//===========================================================================================
// 测试多个Realm认证
// 创建认证对象,并设置认证策略
// MyModularRealmAuthenticator-自己重写的需要认证对象
MyModularRealmAuthenticator modularRealmAuthenticator = new MyModularRealmAuthenticator();
modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
defaultWebSecurityManager.setAuthenticator(modularRealmAuthenticator);
List<Realm> list = new ArrayList<>();
list.add(nameRealm);
list.add(poneRealm);
list.add(emailRealm);
defaultWebSecurityManager.setRealms(list);
//===========================================================================================
// 5.设置rememberme
defaultWebSecurityManager.setRememberMeManager(rememberMeManager());
// 6.返回
return defaultWebSecurityManager;
}
// 配置shiro内置过滤器拦截范围
@Bean
public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
// 设置不认证可以访问的资源
definition.addPathDefinition("/myController/userLogin","anon");
definition.addPathDefinition("/myController/login","anon");
// 设置登出过滤器
definition.addPathDefinition("/logout","logout");
// 设置需要登录认证的拦截资源
definition.addPathDefinition("/**","authc");
definition.addPathDefinition("/**","user");
return definition;
}
// 创建shiro的cookie管理对象
private RememberMeManager rememberMeManager() {
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
return cookieRememberMeManager;
}
// cookie属性设置
private Cookie rememberMeCookie() {
SimpleCookie cookie = new SimpleCookie("rememberMe");
// 设置跨域
//cookie.setDomain(domain);
cookie.setPath("/");
cookie.setHttpOnly(true);
cookie.setMaxAge(30*24*60*60);
return cookie;
}
}
###controller
package com.lix.shiro.controller;
import com.lix.shiro.config.MyUsernamePasswordToken;
import com.lix.shiro.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("myController")
@Slf4j
public class UserController {
@GetMapping("login")
public String login() {
return "login";
}
@GetMapping("userLogin")
public String userLogin(User user,
String loginType,
HttpSession session,
@RequestParam(defaultValue = "false")Boolean rememberMe){
log.info("是否记住密码="+rememberMe);
// 1.获取subject对象
Subject subject = SecurityUtils.getSubject();
// 2.封装数据到token
// AuthenticationToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword(),rememberMe);
MyUsernamePasswordToken token = new MyUsernamePasswordToken(user.getUsername(), user.getPassword(), loginType);
// 3.调用login方法进行认证
try {
subject.login(token);
session.setAttribute("user",token.getPrincipal().toString());
log.info("登录成功!");
}catch (UnknownAccountException uae) {
// 用户名未知...
log.info("用户不存在!");
} catch (IncorrectCredentialsException ice) {
// 凭据不正确,例如密码不正确 ...
log.info("密码不正确!");
} catch (LockedAccountException lae) {
// 用户被锁定,例如管理员把某个用户禁用...
log.info("用户被锁定!");
} catch (ExcessiveAttemptsException eae) {
// 尝试认证次数多余系统指定次数 ...
log.info("尝试认证次数过多,请稍后重试!");
} catch (AuthenticationException ae) {
// 其他未指定异常
log.info("未知异常!");
}
return "main";
}
@GetMapping("userLoginRm")
public String userLoginRm(HttpSession session){
Subject subject = SecurityUtils.getSubject();
String s = subject.getPrincipal().toString();
log.info("===记住账号密码验证测试===");
log.info("subject账号信息:"+s);
return "main";
}
@RequiresRoles("role:admin")
@GetMapping("userLoginRole")
@ResponseBody
public String userLoginRole(){
log.info("测试角色认证成功");
return "测试角色认证成功";
}
@RequiresPermissions("user:add")
@GetMapping("userLoginPermissions")
@ResponseBody
public String userLoginPermissions(){
log.info("测试权限认证成功");
return "测试权限认证成功";
}
}
###html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en" xml:th="http://www.thymeleaf.org" >
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Shiro登录认证页面</h1>
<br>
<form action="/myController/userLogin">
<div>账号:<input type="text" name="username" value=""></div>
<div>密码:<input type="password" name="password" value=""></div>
<table>
<td>用户类型</td>
<td>
<select name="loginType" id="loginType">
<option value="Name">用户名登录</option>
<option value="Phone">手机号登录</option>
<option value="Email">邮箱登录</option>
</select>
</td>
</table>
<div>记住用户:<input type="checkbox" name="rememberMe" value="true"></div>
<div><input type="submit" value="登录"></div>
</form>
</body>
</html>
完整代码跳转地址