Shiro
简介
1.1什么是Shiro?
Apache Shiro是一个Java的安全(权限)框架。Shiro可以非常容易的开发出足够好的应用,其不仅可以用在javaSE环境,也可以用在JavaEE环境。Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等。
1.2有什么功能
Authentication:身份认证、登录,验证用户是不是拥有相应的身份;Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限,即判断用户能否进行什么操作,如:验证某个用户是否拥有某个角色,或者细粒度的验证某个用户对某个资源是否具有某个权限!Session Manager:会话管理,即用户登录后就是第一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通的JavaSE环境,也可以是Web环境;Cryptography:加密,保护数据的安全性,如密码加密存储到数据库中,而不是明文存储;Web Support: Web支持,可以非常容易的集成到Web环境;Caching:缓存,比如用户登录后,其用户信息,拥有的角色、权限不必每次去查,这样可以提高效率Concurrency: Shiro支持多线程应用的并发验证,即,如在一个线程中开启另一个线程,能把权限自动的传 播过去Testing:提供测试支持;Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了
shiro 架构
Subject(the current user):
获得Subject
Subject currentUser
= SecurityUtils
.getSubject();
通过当前用户拿到session
Session session
= currentUser
.getSession();
判断当前方法是否被认证
if (!currentUser
.isAuthenticated()) {...}
获得当前用户的认证
currentUser
.getPrincipal()
用户是否拥有角色
currentUser
.hasRole("schwartz")
获得当前用户的权限
currentUser
.isPermitted("lightsaber:wield")
注销
currentUser
.logout();
SecurityManager
Realm
Shiro集成SpringBoot
1.导入Shiro的整合依赖
<dependencies>
<dependency>
<groupId>org.apache.shiro
</groupId>
<artifactId>shiro-spring
</artifactId>
<version>1.4.1
</version>
</dependency>
<dependency>
<groupId>org.thymeleaf
</groupId>
<artifactId>thymeleaf-spring5
</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras
</groupId>
<artifactId>thymeleaf-extras-java8time
</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot
</groupId>
<artifactId>spring-boot-starter-web
</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot
</groupId>
<artifactId>spring-boot-starter-test
</artifactId>
<scope>test
</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage
</groupId>
<artifactId>junit-vintage-engine
</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
2.编写核心配置(ShiroConfig和UserRealm)
ShiroConfig
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean
getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager
){
ShiroFilterFactoryBean bean
= new ShiroFilterFactoryBean();
bean
.setSecurityManager(defaultWebSecurityManager
);
return bean
;
}
@Bean(name
= "securityManager")
public DefaultWebSecurityManager
getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm
){
DefaultWebSecurityManager securityManager
= new DefaultWebSecurityManager();
securityManager
.setRealm(userRealm
);
return securityManager
;
}
@Bean
public UserRealm
userRealm(){
return new UserRealm();
}
}
UserRealm
public class UserRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo
doGetAuthorizationInfo(PrincipalCollection principalCollection
) {
System
.out
.println("执行了=>授权");
return null
;
}
@Override
protected AuthenticationInfo
doGetAuthenticationInfo(AuthenticationToken authenticationToken
) throws AuthenticationException
{
System
.out
.println("执行了=>认证");
return null
;
}
}
Shiro实现登录拦截和用户认证
MyController
@Controller
public class MyController {
@RequestMapping({"/","/index"})
public String
toIndex(Model model
){
model
.addAttribute("msg","hello-Shiro");
return "index";
}
@RequestMapping("/user/add")
public String
add(){
return "user/add";
}
@RequestMapping("/user/update")
public String
update(){
return "user/update";
}
@RequestMapping("/toLogin")
public String
toLogin(){
return "login";
}
@RequestMapping("/login")
public String
login(String username
,String password
,Model model
){
Subject subject
= SecurityUtils
.getSubject();
UsernamePasswordToken token
= new UsernamePasswordToken(username
, password
);
try{
subject
.login(token
);
return "index";
}catch (UnknownAccountException e
){
model
.addAttribute("msg","用户名错误");
return "login";
}catch (IncorrectCredentialsException e
){
model
.addAttribute("msg","密码错误");
return "login";
}
}
}
ShiroConfig
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean
getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager
){
ShiroFilterFactoryBean bean
= new ShiroFilterFactoryBean();
bean
.setSecurityManager(defaultWebSecurityManager
);
Map
<String,String> filterMap
= new LinkedHashMap<>();
filterMap
.put("/user/add","authc");
filterMap
.put("/user/update","authc");
bean
.setFilterChainDefinitionMap(filterMap
);
bean
.setLoginUrl("/toLogin");
return bean
;
}
@Bean(name
= "securityManager")
public DefaultWebSecurityManager
getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm
){
DefaultWebSecurityManager securityManager
= new DefaultWebSecurityManager();
securityManager
.setRealm(userRealm
);
return securityManager
;
}
@Bean
public UserRealm
userRealm(){
return new UserRealm();
}
}
UserRealm
package com
.kuang
.config
;
import org
.apache
.shiro
.authc
.*
;
import org
.apache
.shiro
.authz
.AuthorizationInfo
;
import org
.apache
.shiro
.realm
.AuthorizingRealm
;
import org
.apache
.shiro
.subject
.PrincipalCollection
;
public class UserRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo
doGetAuthorizationInfo(PrincipalCollection principals
) {
System
.out
.println("执行了=>授权");
return null
;
}
@Override
protected AuthenticationInfo
doGetAuthenticationInfo(AuthenticationToken token
) throws AuthenticationException
{
System
.out
.println("执行了=>认证");
String name
="root";
String password
="123456";
UsernamePasswordToken userToken
=(UsernamePasswordToken
)token
;
if(!userToken
.getUsername().equals(name
)){
return null
;
}
return new SimpleAuthenticationInfo("",password
,"");
}
}
Shiro整合mybatis
1.添加pom依赖
<dependencies>
<dependency>
<groupId>org.projectlombok
</groupId>
<artifactId>lombok
</artifactId>
<version>1.16.10
</version>
</dependency>
<dependency>
<groupId>mysql
</groupId>
<artifactId>mysql-connector-java
</artifactId>
</dependency>
<dependency>
<groupId>log4j
</groupId>
<artifactId>log4j
</artifactId>
<version>1.2.17
</version>
</dependency>
<dependency>
<groupId>com.alibaba
</groupId>
<artifactId>druid
</artifactId>
<version>1.1.12
</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot
</groupId>
<artifactId>mybatis-spring-boot-starter
</artifactId>
<version>2.1.0
</version>
</dependency>
<dependency>
<groupId>org.apache.shiro
</groupId>
<artifactId>shiro-spring
</artifactId>
<version>1.4.1
</version>
</dependency>
<dependency>
<groupId>org.thymeleaf
</groupId>
<artifactId>thymeleaf-spring5
</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras
</groupId>
<artifactId>thymeleaf-extras-java8time
</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot
</groupId>
<artifactId>spring-boot-starter-web
</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot
</groupId>
<artifactId>spring-boot-starter-test
</artifactId>
<scope>test
</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage
</groupId>
<artifactId>junit-vintage-engine
</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
2.配置application.yml
spring:
datasource:
username: root
password: 123456
url: jdbc
:mysql
://localhost
:3306/mybats
?serverTimezone=UTC
&useUnicode=true
&characterEncoding=utf
-8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testwhileIdle: true
testOnBorrow: false
testonReturn: false
poolPreparedStatements: true
filters: stat
,wall
,log4j
maxPoolPreparedstatementPerconnectionSize: 20
useGlobalDatasourcestat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
3.配置application.properties
#绑定mybatis
mybatis.type-aliases-package=com.kuang.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
4.创建pojo/User类
import lombok
.AllArgsConstructor
;
import lombok
.Data
;
import lombok
.NoArgsConstructor
;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id
;
private String username
;
private String password
;
private String perms
;
}
5.创建mapper/UserMapper(Interface)
import org
.apache
.ibatis
.annotations
.Mapper
;
import org
.springframework
.stereotype
.Repository
;
@Repository
@Mapper
public interface UserMapper {
public User
queryUserByName(String name
);
}
6.配置mapper/UserMapper.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.kuang.mapper.UserMapper">
<select id="queryUserByName" resultType="User">
select * from mybats.users where username=#{username};
</select>
</mapper>
7.创建Service/UserService(Interface)
import com
.wzd
.pojo
.User
;
public interface UserService {
public User
queryUserByName(String name
);
}
8.实现UserService即创建Service/UserServiceImpl
import com
.wzd
.mapper
.UserMapper
;
import com
.wzd
.pojo
.User
;
import org
.springframework
.beans
.factory
.annotation
.Autowired
;
import org
.springframework
.stereotype
.Service
;
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper
;
@Override
public User
queryUserByName(String name
) {
return userMapper
.queryUserByName(name
);
}
}
9.配置config/UserRealm(先认证,然后授权)
import com
.wzd
.pojo
.User
;
import com
.wzd
.service
.UserService
;
import org
.apache
.shiro
.SecurityUtils
;
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
.subject
.Subject
;
import org
.springframework
.beans
.factory
.annotation
.Autowired
;
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService
;
@Override
protected AuthorizationInfo
doGetAuthorizationInfo(PrincipalCollection principals
) {
System
.out
.println("执行了=>授权");
SimpleAuthorizationInfo info
= new SimpleAuthorizationInfo();
Subject subject
= SecurityUtils
.getSubject();
User currentUser
=(User
) subject
.getPrincipal();
info
.addStringPermission(currentUser
.getPerms());
return info
;
}
@Override
protected AuthenticationInfo
doGetAuthenticationInfo(AuthenticationToken token
) throws AuthenticationException
{
System
.out
.println("执行了=>认证");
UsernamePasswordToken userToken
=(UsernamePasswordToken
)token
;
User user
= userService
.queryUserByName(userToken
.getUsername());
if(user
==null
){
return null
;
}
return new SimpleAuthenticationInfo(user
,user
.getPassword(),"");
}
}
10.配置config/ShiroConfig
import org
.apache
.shiro
.spring
.web
.ShiroFilterFactoryBean
;
import org
.apache
.shiro
.web
.mgt
.DefaultWebSecurityManager
;
import org
.springframework
.beans
.factory
.annotation
.Qualifier
;
import org
.springframework
.context
.annotation
.Bean
;
import org
.springframework
.context
.annotation
.Configuration
;
import java
.util
.LinkedHashMap
;
import java
.util
.Map
;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean
getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager
){
ShiroFilterFactoryBean bean
= new ShiroFilterFactoryBean();
bean
.setSecurityManager(defaultWebSecurityManager
);
Map
<String,String> filterMap
= new LinkedHashMap<>();
filterMap
.put("/user/add","perms[user:add]");
filterMap
.put("/user/update","perms[user:update]");
filterMap
.put("/uesr/*","authc");
bean
.setFilterChainDefinitionMap(filterMap
);
bean
.setLoginUrl("/toLogin");
//未授权页面
bean
.setUnauthorizedUrl("/noauth");
return bean
;
}
//DefultWebSecurityManager
2
@Bean(name
= "securityManager")
public DefaultWebSecurityManager
getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm
){
DefaultWebSecurityManager securityManager
= new DefaultWebSecurityManager();
//关联UserRealm
securityManager
.setRealm(userRealm
);
return securityManager
;
}
//创建Realm对象,需要自定义类:
1
@Bean
public UserRealm
userRealm(){
return new UserRealm();
}
}
11配置未授权跳转Controller
@ResponseBody
@RequestMapping("/noauth")
public String
unauthorized(){
return "没有权限,无法访问";
}
Shiro整合thymeleaf
1.配置pom.xml
<dependency>
<groupId>com.github.theborakompanioni
</groupId>
<artifactId>thymeleaf-extras-shiro
</artifactId>
<version>2.0.0
</version>
</dependency>
2.前端加命名空间并配置
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title
</title>
</head>
<body>
<h1>首页
</h1>
<div th:if="${session.loginUser==null}">
<a th:href="@{/toLogin}">登录
</a>
</div>
<p th:text="${msg}"></p>
<hr>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add
</a>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update
</a>
</div>
</body>
</html>
3.UserRealm传入Session
Subject currentSubject
= SecurityUtils
.getSubject();
Session session
= currentSubject
.getSession();
session
.setAttribute("loginUser",user
);