Shiro】
十、Spring Boot整合Shiro实现登录认证
整体项目结构。省略数据访问层相关代码,使用固定数据进行认证。
1、添加依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.13.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--mybatis的启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!--数据库的驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
</dependencies>
2、编写配置文件
新建application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/rbac1?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
shiro:
loginUrl: /login
3、新建实体类
新建com.bjsxt.pojo.User
public class User {
private Long id;
private String username;
private String password;
// 省略getter和setter
// 省略构造方法
}
4、编写业务层代码
新建com.bjsxt.service.UserService
public interface AdminService {
Admin findOne(String uname);
}
实现类
@Service
public class AdminServiceImpl implements AdminService {
@Autowired
private AdminMapper adminMapper;
@Override
public Admin findOne(String uname) {
return adminMapper.selectOne(uname);
}
}
5、自定义Realm
新建com.bjsxt.realm.MyRealm编写认证逻辑
@Component
public class MyRealm extends AuthorizingRealm {
@Autowired
private AdminService adminService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String name = authenticationToken.getPrincipal().toString();
Admin admin = adminService.findOne(name);
if(admin!=null){
AuthenticationInfo info=new SimpleAuthenticationInfo(name,admin.getPwd(), ByteSource.Util.bytes(admin.getSalt()),"sxt");
return info;
}
return null;
}}
6、编写配置
新建com.bjsxt.config.ShiroConfig,编写配置
@Configuration
public class SApplicationConfig {
@Autowired
private MyRealm myRealm;
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");
matcher.setHashIterations(3);
myRealm.setCredentialsMatcher(matcher);
defaultWebSecurityManager.setRealm(myRealm);
return defaultWebSecurityManager;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
definition.addPathDefinition("/doLogin", "anon");
definition.addPathDefinition("/login", "anon");
definition.addPathDefinition("/**", "authc");
return definition;
}
}
7、编写控制器
新建com.bjsxt.controller.UserController
@Controller
public class AdminController {
@RequestMapping("/{path}")
public String getUrl(@PathVariable String path){
System.out.println("iii");
return path;
}
@RequestMapping("/doLogin")
public String login(String uname,String pwd){
Subject subject = SecurityUtils.getSubject();
try {
subject.login(new UsernamePasswordToken(uname,pwd));
return "main" ;
} catch (AuthenticationException e) {
System.out.println("失败");
return "login";
}
}
}
8、编写启动类
新建com.bjsxt.ShiroApplication
@SpringBootApplication
@MapperScan("com.bjsxt.mapper")
public class ShiroApplication {
public static void main(String[] args) {
SpringApplication.run(ShiroApplication.class,args);
}
}
9、编写页面
9.1 编写登录页面
新建templates/login.html
<form action="/doLogin" method="post">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password" /><br/>
<input type="submit" value="登录"/>
</form>
9.2 编写主页面
新建templates/login.html。页面中虽然编写内容
<body>
index.html
</body>
十一、凭证匹配器
1、修改ShiroConfig
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
hashedCredentialsMatcher.setHashIterations(2);
myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
manager.setRealm(myRealm);
return manager;
}
2、修改UserServiceImpl
模拟从数据库查询出来的密码是加密的密码。
此加密数据是”admin”加盐”123”迭代两次加密的结果。
return new User(1L,"admin","6bdae6366c1e46d541eb0ca9547d974c");
3、修改MyRealm
修改MyRealm中doGetAuthenticationInfo方法。把盐添加进去。
AuthenticationInfo info = new SimpleAuthenticationInfo(token.getPrincipal(),user.getPassword(), ByteSource.Util.bytes("123"),user.getUsername());
十二、remember me实现
1、修改ShiroConfig
秘钥长度为16位,使用的时AES加密。
@Configuration
public class ShiroConfig {
@Autowired
private MyRealm myRealm;
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
hashedCredentialsMatcher.setHashIterations(2);
myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
manager.setRealm(myRealm);
manager.setRememberMeManager(rememberMeManager());
return manager;
}
/**
* cookie 属性设置
*/
public SimpleCookie rememberMeCookie()
{
SimpleCookie cookie = new SimpleCookie("rememberMe");
// cookie.setDomain(domain);
cookie.setPath("/");
cookie.setHttpOnly(true);
cookie.setMaxAge(30 * 24 * 60 * 60);
return cookie;
}
/**
* 记住我
*/
public CookieRememberMeManager rememberMeManager()
{
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
cookieRememberMeManager.setCipherKey(“1234567890987654”);
return cookieRememberMeManager;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
definition.addPathDefinition("/doLogin", "anon");
definition.addPathDefinition("/logout", "logout");
definition.addPathDefinition("/**", "user");
return definition;
}
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
2、修改控制器方法
修改控制doLogin方法,在UsernamePasswordToken中添加第三个参数。表示是否启用remember me功能。
此功能应该在页面中添加一个复选框,代码中直接假设用户勾选了复选框
@RequestMapping("/doLogin")
public String showLogin(User user){
Subject subject = SecurityUtils.getSubject();
try {
//添加第三个参数,表示是否启用rememberme功能
subject.login(new UsernamePasswordToken(user.getUsername(),user.getPassword(),true));
return "redirect:/showIndex";
} catch (Exception e) {
e.printStackTrace();
}
return "redirect:/showLogin";
}
十三、退出实现
1、修改配置类
修改ShiroConfig类,添加logout filter 对应的url。
红色部分为新增内容。
2、修改主页面
在index.html页面中添加超链接。跳转到/logout后会由shiro内置filter进行拦截。
<body>
index.html
<a href="/logout">退出</a>
</body>
十四、授权
授权就是判断认证用户是否具有指定角色或指定权限。
Shiro可以和JSP整合也可以和Thymeleaf整合,我们讲解SpringBoot的视图技术Thymeleaf整合Shiro。
只要是授权就执行Realm的doGetAuthorizationInfo进行判断,而触发doGetAuthorizationInfo的方式,常用的就两种:
- (1)在页面中通过shiro:xxxx 属性进行判断
- (2)在java代码中通过注解@RequiresXXX/config文件中配置
1、thymeleaf中常用属性
需要在html页面中<html>添加属性
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
1.1 shiro:user=””
认证通过或已记住的用户。
1.2 shiro:authenticated=””
认证通过的用户。不包含记住的用户。
1.3 shiro:principal
输出认证用户信息。<shiro:principal/>
1.4 shiro:hasRole="admin"
判断是否具有指定角色。
1.5 shiro:lacksRole="admin"
判断是否具有指定角色。
1.6 shiro:hasAllRoles="role1,role2"
判断指定角色用户是否都具有。
1.7 shiro:hasAnyRoles="role1,role2"
只要用户具有其中一个角色就表示判断通过。
1.8 shiro:hasPermission="userInfo:add"
是否具有指定权限。
1.9 shiro:lacksPermission="userInfo:del"
是否不具有指定权限
1.10 shiro:hasAllPermissions="userInfo:view, userInfo:add"
是否全具有指定权限。
1.11 shiro:hasAnyPermissions="userInfo:view, userInfo:del"
只要有其中任何一个权限即可。