整合
1.搞一个@Configuration注解的类,有这个注解项目启动自动执行
描述:这个类自动拦截过滤所有的请求做不同的处理,当调用Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);的时候会触发SecurityManager安全管理器的Authenticator认证器的doGetAuthenticationInfo方法来认证
/**
* @ fileName:SpringShiroConfig
* @ description:
* @ author:zhz
* @ createTime:2022/3/9 14:55
* @ version:1.0.0
*/
@Configuration //相当于过去的xml <beans>
public class SpringShiroConfig {
/**
* 配置ShiroFilterFactoryBean过滤器 拦截到所有请求,然后做不同的处理
* @return
*/
@Bean //<bean id=shiroFilter class=org.apache.shiro.spring.web.ShiroFilterFactoryBean>
public ShiroFilterFactoryBean shiroFilter(){
//实例化对象
ShiroFilterFactoryBean shiroFilterFactoryBean =
new ShiroFilterFactoryBean();
//配置当前shiro使用securityManager
//<property name="securityManager" ref="securityManager"/>
shiroFilterFactoryBean.setSecurityManager(securityManager());
//配置未认证通过认证跳转的地址
shiroFilterFactoryBean.setLoginUrl("/html/login.html");
//认证成功后,默认跳转地址
//shiroFilterFactoryBean.setSuccessUrl("");
//访问未授权的url时跳转地址
shiroFilterFactoryBean.setUnauthorizedUrl("/html/unauthorized.html");
//定义放行拦截规则 =号后面配置任意一个字符串都是代表一个底层filter一定不能写错
// /html/login.html=anon // anon 不需要认证,就可以访问地址 放行地址
// /user/logout = logout // 用户注销配置
/* # some example chain definitions:
/admin*//** = authc, roles[admin] //当地址栏地址含有/admin/** authc 需要认证,必须登录, roles[admin] 必须拥有admin角色才可以访问
/docs/** = authc, perms[document:read] //当地址栏含有/docs/** authc 需要认证,必须登录, perms[document:read] 必须拥有document:read 权限才可以访问
/** = authc // 除了上面单独的配置之外,其他请求全部需要认证 并且该配置必须在其他配置之后
# more URL-to-FilterChain definitions here*/
Map filterChainDefinitionMap =new LinkedHashMap();//hashMap 不要使用new HashMap() 无序的 LinkedHashMap获取时会按照放入的顺序
//放行配置
filterChainDefinitionMap.put("/html/login.html","anon");
filterChainDefinitionMap.put("/user/login","anon");
filterChainDefinitionMap.put("/user/add","anon");
filterChainDefinitionMap.put("/css/**","anon");
filterChainDefinitionMap.put("/js/**","anon");
filterChainDefinitionMap.put("/imgs/**","anon");
//登出 前面地址随便写,和地址栏对应 后面 logout必须这样写
filterChainDefinitionMap.put("/logout","logout");
//全部需要认证
filterChainDefinitionMap.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 实例化SecurityManager 当前用户的幕后主使,所有认证授权等功能都是它来完成
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager defaultWebSecurityManager =
new DefaultWebSecurityManager();
//依赖注入自定义realm
defaultWebSecurityManager.setRealm(myCustomRealm());
return defaultWebSecurityManager;
}
/**
* 实例化自定义realm获取安全数据
* @return
*/
@Bean
public MyCustomRealm myCustomRealm(){
MyCustomRealm myCustomRealm = new MyCustomRealm();
myCustomRealm.setCredentialsMatcher(credentialsMatcher());
return myCustomRealm;
}
/**
* 实例化HashedCredentialsMatcher 配置加密算法和哈希次数
* @return
*/
@Bean
public HashedCredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher =
new HashedCredentialsMatcher();
//设置加密算法
hashedCredentialsMatcher.setHashAlgorithmName(BussinessConstant.CredentialsMatcher.HASHEDAGRITHMNAME);
//设置哈希次数 增加破解难度
hashedCredentialsMatcher.setHashIterations(BussinessConstant.CredentialsMatcher.HASHEDINTERATIONS);
return hashedCredentialsMatcher;
}
}
2.认证和授权方法
描述:特定情况下触发这两个方法的时候
触发情况:
1.doGetAuthenticationInfo执行时机如下
当调用Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);
2.doGetAuthorizationInfo执行时机有三个,如下:
1、subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去调用这个是否有什么角色或者是否有什么权限的时候;
2、@RequiresRoles(“admin”) :在方法上加注解的时候;
3、[@shiro.hasPermission name = “admin”][/@shiro.hasPermission]:在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候。
方法:
/**
* @ fileName:MyCustomRealm
* @ description:
* @ author:zhz
* @ createTime:2022/3/9 15:04
* @ version:1.0.0
*/
public class MyCustomRealm extends AuthorizingRealm {
@Resource
private UserService userService;
/**
* 获取角色和权限数据 做授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//该方法执行时,认证一定是通过的,如果认证不过,该方法不会执行
// 获取认证时放入的第一个参数
User user = (User)principalCollection.getPrimaryPrincipal();
String userName = user.getUserName();
SimpleAuthorizationInfo simpleAuthorizationInfo =new SimpleAuthorizationInfo();
List<Role> roleList = userService.queryRoleListByUserName(userName);
List<Menu> menuList = userService.queryMenuListByUserName(userName);
//添加角色
if(!CollectionUtils.isEmpty(roleList)){
for (Role role : roleList) {
simpleAuthorizationInfo.addRole(role.getRoleKey());
}
}
//添加权限
if(!org.springframework.util.CollectionUtils.isEmpty(menuList)){
for (Menu menu : menuList) {
if(!StringUtils.hasLength(menu.getPerms())){
simpleAuthorizationInfo.addStringPermission(menu.getPerms());
}
}
}
return simpleAuthorizationInfo;
}
/**
* 获取用户 做认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取用户名
String userName = (String)authenticationToken.getPrincipal();
//根据用户获取用户信息
User user = userService.queryUserByUserName(userName);
//如果对象为空,直接账号错误 代码终止运行
if(user==null){
throw new AccountException();
}
//获取加密密码
String password = user.getPassword();
//获取盐值
String salt = user.getSalt();
return new SimpleAuthenticationInfo(user,//第一参数 放主要的信息(用户信息) 当认证成功后,可以通过Subject获取
password,//第二参数 加密密码 用户注册时,要使用shiro提供的加密算法SHA-512,带上密码和随机盐值经过10次哈希后的结果
ByteSource.Util.bytes(salt),//第三个参数 随机盐值
this.getName()); }
}
加密工具:
public class BussinessConstant {
/**
* 加密专用常量
*/
public interface CredentialsMatcher{
String HASHEDAGRITHMNAME="SHA-512";
int HASHEDINTERATIONS=1024;
}
}
登陆细节:
1.触发认证的登录方法:
/**
* 用户登录
* @param userName
* @param password
*/
@PostMapping("login")
public String login(String userName,String password){
//收集用户信息
UsernamePasswordToken usernamePasswordToken =
new UsernamePasswordToken(userName, password);
//获取当前主体(当前用户)
Subject currentUser = SecurityUtils.getSubject();
String errorInfo = "";
try {
//登录
currentUser.login(usernamePasswordToken);
//获取认证成功的用户信息
User user = (User)currentUser.getPrincipal();
//获取安全session
Session session = currentUser.getSession();
//存入用户信息
session.setAttribute("userInfo",user);
//认证成功后 重定向到部门查询
return "redirect:/dept/queryAll";
}catch (AccountException e) {
e.printStackTrace();
errorInfo= "1";
}catch (IncorrectCredentialsException e) {
e.printStackTrace();
errorInfo= "2";
} catch (AuthenticationException e) {
e.printStackTrace();
errorInfo= "3";
}
return "redirect:/html/login.html?errorInfo="+errorInfo;
}
2.这里使用的是实体类做sql执行返回值,需要设置实体类和实体类对应的属性,注意sql查询操作的元素要与实体类属性名字相同,强制的,一般sql都是复杂的不会相同,有两个方法可以是两者相同
1.通过resultMap标签设置实体类和sql查询的数据
<!--<resultMap id="map1" type="com.aaa.sbm.entity.Role">
<id column="role_id" property="roleId"></id>
<result></result>
</resultMap>-->
2. 改别名
select role_id roleId,role_name as roleName,role_key roleKey from sys_role
<!--namespace 1,绑定接口 2,隔离语句-->
<mapper namespace="com.aaa.sbm.mapper.UserDao">
sql语句
<mapper namespace="com.aaa.sbm.mapper.UserDao">
<select id="queryAll" resultType="com.aaa.sbm.entity.User">
select user_id userId,user_name userName,login_Name loginName,password,salt
from sys_user where del_flag=0 and status=0
<if test="userName!=null and userName!=''">
and user_name=#{userName}
</if>
</select>
授权: 获取角色和权限数据
这边是通过方法上的限制去处理权限,当要访问某个方法得时候会先去查看你这个用户得权限,有对应得授权方法,通过方法上的 这三种注解
@RequiresPermissions(“dept:query”) // 判断当前用户是否拥有权限dept:query,如果有就可以执行该方法
@RequiresPermissions(“dept:queryById”) //判断当前用户是否拥有角色代理商,如果有就可以执行该方法
@RequiresRoles(“guanliyuan”) //判断当前用户是否拥有角色管理员,如果有就可以执行该方法
去触发查询权限函数
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//该方法执行时,认证一定是通过的,如果认证不过,该方法不会执行
// 获取认证时放入的第一个参数
User user = (User)principalCollection.getPrimaryPrincipal();
String userName = user.getUserName();
SimpleAuthorizationInfo simpleAuthorizationInfo =new SimpleAuthorizationInfo();
List<Role> roleList = userService.queryRoleListByUserName(userName);
List<Menu> menuList = userService.queryMenuListByUserName(userName);
//添加角色
if(!CollectionUtils.isEmpty(roleList)){
for (Role role : roleList) {
simpleAuthorizationInfo.addRole(role.getRoleKey());
}
}
//添加权限
if(!org.springframework.util.CollectionUtils.isEmpty(menuList)){
for (Menu menu : menuList) {
if(!StringUtils.hasLength(menu.getPerms())){
simpleAuthorizationInfo.addStringPermission(menu.getPerms());
}
}
}
return simpleAuthorizationInfo;
}
菜单角色权限sql
1查询用户角色权限
<select id="queryRoleListByUserName" resultType="com.aaa.sbm.entity.Role">
select role_id roleId,role_name as roleName,role_key roleKey from sys_role r where del_flag=0 and exists
(
select 1 from sys_user_role ur where ur.user_id=
(select user_id from sys_user where user_name=#{userName} and del_flag=0 and status=0)
and r.role_id=ur.role_id
)
</select>
2.查询用户菜单权限
1通过exists优化sql是关键,也是值得去学习了解得地方
<select id="queryMenuListByUserName" resultType="com.aaa.sbm.entity.Menu">
select menu_id menuId,menu_name menuName,perms from sys_menu m where visible=0 and exists(
select 1 from sys_role_menu rm where exists(
select 1 from sys_user_role ur where user_id=
(select user_id from sys_user where user_name=#{userName} and del_flag=0 and status=0)
and rm.role_id=ur.role_id
) and rm.menu_id=m.menu_id
)
</select>