Java安全框架shiro(一)

1. shiro简介

**Apache Shiro™**是一个功能强大且易于使用的Java安全框架,用于执行身份验证,授权,加密和会话管理。使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移动应用程序到最大的Web和企业应用程序。

2. shiro四个核心模块

**Authentication**:身份认证/登录,验证用户是不是拥有相应的身份;

**Authorization**:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

**Session Manager**:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;

**Cryptography**:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

3. shrio三大核心组件

**Subject**:主体,可以看到主体可以是任何可以与应用交互的“用户”;

**SecurityManager**:相 当 于 SpringMVC 中 的 DispatcherServlet

**Realm**:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;

4. shiro实现原理

应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager; 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。

5.实现

springboot+shiro整合

springboot+mybatisPlus+shiro加密-认证-授权

5.1、 数据库

~~~

--用户表

drop table if exists t_user;

create table t_user(

user_id int primary key auto_increment,

username varchar(20) not null unique,

password varchar(255) not null,

salt varchar(255) not null

);

insert into t_user(username,password,salt) values("tsk","1234","c79e3988-e93b-47ee-a344-46359567e108");

insert into t_user(username,password,salt) values("ylp","1234","c79e3988-e93b-47ee-a344-46359567e109");

--角色表

drop table if exists t_role;

create table t_role(

role_id int primary key auto_increment,

role_name varchar(200) not null unique

);

insert into t_role(role_name) values("seller"),("emp");

--权限表

drop table if exists t_permission;

create table t_permission(

permission_id int primary key auto_increment,

permission_name varchar(200) not null unique

);

insert into t_permission(permission_name) values("emp:query"),("emp:add"),("emp:delete"),("emp:update");

--用户角色表

drop table if exists t_user_role;

create table t_user_role(

user_id int references t_user(user_id),

role_id int references t_role(role_id)

);

insert into t_user_role(user_id,role_id) values(1,1),(2,2);

--角色权限表

drop table if exists t_role_permission;

create table t_role_permission(

role_id int references t_role(role_id),

permission_id int references t_permission(permission_id)

);

insert into t_role_permission(role_id, permission_id) values (1,1),(1,4),(2,1);

查询用户

select * from t_user where username="tsk";

查询角色

select role_name from t_role r

inner join t_user_role ur on r.role_id=ur.role_id

inner join t_user u on u.user_id=ur.user_id

where u.username="tsk";

查询权限

select permission_name from t_permission p

inner join t_role_permission rp on p.permission_id=rp.permission_id

inner join t_user_role ur on ur.role_id=rp.role_id

inner join t_user u on u.user_id=ur.user_id

where u.username="tsk";

~~~

5.2、 pom.xml

~~~

<dependencies>

<!--spring web-->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<!--thymeleaf-->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-thymeleaf</artifactId>

</dependency>

<!--mysql-->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.47</version>

</dependency>

<!--lombok-->

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

<optional>true</optional>

</dependency>

<!--mybatis plus-->

<dependency>

<groupId>com.baomidou</groupId>

<artifactId>mybatis-plus-boot-starter</artifactId>

<version>3.1.1</version>

</dependency>

<!--shiro-->

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-spring</artifactId>

<version>1.4.0</version>

</dependency>

<!--tomcat-->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-tomcat</artifactId>

<scope>provided</scope>

</dependency>

<!--test-->

<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>

~~~

## application.properties

~~~

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/mmdb

spring.datasource.username=root

spring.datasource.password=1234

spring.thymeleaf.cache=false

~~~

5.3 、实体类

~~~

@Data

@AllArgsConstructor

@NoArgsConstructor

@TableName("t_user")

public class User {

@TableField("user_id")

private Integer userId;

private String username;

private String password;

private String salt;

}

~~~

~~~

@Data

@AllArgsConstructor

@NoArgsConstructor

@TableName("t_role")

public class Role {

@TableId("role_id")

private Integer roleId;

@TableField("role_name")

private String roleName;

}

~~~

~~~

@Data

@AllArgsConstructor

@NoArgsConstructor

@TableName("t_permission")

public class Permission {

@TableId("permission_id")

private Integer permissionId;

@TableField("permission_name")

private String permissionName;

}

~~~

5.4、 Dao

~~~

@Mapper

public interface UserDao extends BaseMapper<User> {

}

~~~

~~~

@Mapper

public interface RoleDao extends BaseMapper<Role> {

@Select("select role_name from t_role r " +

"inner join t_user_role ur on r.role_id=ur.role_id " +

"inner join t_user u on u.user_id=ur.user_id " +

"where u.username=#{username}")

public Set<String> findByUsername(@Param("username") String username);

}

~~~

~~~java

@Mapper

public interface PermissionDao extends BaseMapper<Permission> {

@Select("select permission_name from t_permission p " +

"inner join t_role_permission rp on p.permission_id=rp.permission_id " +

"inner join t_user_role ur on ur.role_id=rp.role_id " +

"inner join t_user u on u.user_id=ur.user_id " +

"where u.username=#{username}")

public Set<String> findByUsername(@Param("username") String username);

}

~~~

5.5、 Service

~~~java

public interface UserService {

public User findByUsername(String username);

public int add(User user);

}

~~~

~~~java

public interface RoleService {

public Set<String> findByUsername(String username);

}

~~~

~~~java

public interface PermissionService {

public Set<String> findByUsername(String username);

}

~~~

## 自定义Realm

~~~java

/**

* 自定义Realm

*/

public class MyRealm extends AuthorizingRealm {

@Resource

private UserService userService;

@Resource

private RoleService roleService;

@Resource

private PermissionService permissionService;

/*授权*/

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

//获取用户名

String username = (String)principals.getPrimaryPrincipal();

Set<String> roles = roleService.findByUsername(username);

Set<String> permissions = permissionService.findByUsername(username);

SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);

simpleAuthorizationInfo.addStringPermissions(permissions);

return simpleAuthorizationInfo;

}

/*认证*/

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

//获取用户名

String username = (String)token.getPrincipal();

User user = userService.findByUsername(username);

if(user == null){

return null;

}

return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),this.getName());

}

}

~~~

5.6、shiro配置类

~~~java

@Configuration

public class ShiroConfig {

//声明密码比对器

@Bean

public HashedCredentialsMatcher hashedCredentialsMatcher() {

HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

hashedCredentialsMatcher.setHashAlgorithmName("MD5");

hashedCredentialsMatcher.setHashIterations(SpringTool.SHIRO_JM_COUNT);// 设置加密次数

hashedCredentialsMatcher.setStoredCredentialsHexEncoded(false);

return hashedCredentialsMatcher;

}

//将自己的验证方式(Realm)加入到容器

@Bean

public MyRealm myRealm(){

MyRealm myRealm = new MyRealm();

//配置自定义密码比较器

myRealm.setCredentialsMatcher(hashedCredentialsMatcher());

return myRealm;

}

//给SecurityManager注入自定义Realm

@Bean

public SecurityManager securityManager(){

DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

securityManager.setRealm(myRealm());

return securityManager;

}

//Filter工厂,设置对应的过滤条件和跳转条件

@Bean

public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){

ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

shiroFilterFactoryBean.setSecurityManager(securityManager);

Map<String,String> map = new HashMap<>();

//登出

map.put("/logout","logout");

//user/login允许匿名访问

map.put("/user/login","anon");

//user/query必须认证才能访问

map.put("/user/query","authc");

//user/delete必须认证而且是管理员角色才能访问

map.put("/user/delete","roles[seller]");

//...

//未登陆跳转到对应的地址

shiroFilterFactoryBean.setLoginUrl("/user/login");

//角色和权限不足跳转到对应的地址

shiroFilterFactoryBean.setUnauthorizedUrl("/user/error");

//登出跳转到对应的地址

shiroFilterFactoryBean.setSuccessUrl("/user/login");

shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

return shiroFilterFactoryBean;

}

}

~~~

5.7 、Controller

~~~java

@Controller

@RequestMapping("/user")

public class UserController {

@Resource

private UserService userService;

/*去登陆页面*/

@GetMapping("/login")

public String login(){

return "login";

}

/*去注册页面*/

@GetMapping("/register")

public String register(){

return "register";

}

/*用户注册*/

@PostMapping("/register")

public String register(User user){

String salt = UUID.randomUUID().toString();

user.setSalt(salt);

String password = new Md5Hash(user.getPassword(),salt, SpringTool.SHIRO_JM_COUNT).toBase64();

user.setPassword(password);

int rows = userService.add(user);

if(rows > 0){

return "login";

}else {

return "register";

}

}

/*用户登陆*/

@PostMapping("/login")

public String login(User user){

Subject subject = SecurityUtils.getSubject();

UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());

subject.login(token);

return "main";

}

}

~~~

5.8 、页面模板

~~~html

<!DOCTYPE html>

<html lang="en" xmlns:th="http://www.thymeleaf.rog">

<head>

<meta charset="UTF-8">

<title>注册</title>

</head>

<body>

<h1>--注册--</h1>

<form action="register" method="post">

<p>用户名:<input name="username"></p>

<p>密 码:<input type="password" name="password"></p>

<p><input type="submit" value="注册"></p>

</form>

</body>

</html>

~~~

6、 记住我

`在登录后,可以将用户存在cookie中,下次访问时,可以先不登录,就可以识别身份。在确实需要身份认证时,比如购买,支付或者其他一些重要操作时,再要求用户登录即可,用户体验好。由于可以保持用户信息,系统后台也可以更好的监控、记录用户行为,积累数据。

记住我在登录时刻:Subject.login(token);确定是否要记住我,由登录时的token控制:token.setRememberMe(true);

~~~java

/*用户登陆*/

@PostMapping("/login")

public String login(User user){

Subject subject = SecurityUtils.getSubject();

UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());

//记住我

token.setRememberMe(true);

subject.login(token);

return "main";

}

~~~

上述使用的是默认的cookie,在网页的控制台可以看到默认记住365天。如果想修改cookie的相关配置,则修改shiro的配置类即可

~~~java

// 自定义cookie配置

@Bean

public SimpleCookie simpleCookie(){

//这个参数是cookie的名称

SimpleCookie cookie = new SimpleCookie("rememberMe");

cookie.setHttpOnly(true);

//单位是秒,7天

cookie.setMaxAge(604800);

return cookie;

}

// cookie管理对象;记住我功能,rememberMe管理器

@Bean

public CookieRememberMeManager rememberMeManager(){

CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();

cookieRememberMeManager.setCookie(simpleCookie());

//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)

cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));

return cookieRememberMeManager;

}

//给SecurityManager注入自定义组件

@Bean

public SecurityManager securityManager(){

DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

//给SecurityManager注入自定义realm

securityManager.setRealm(myRealm());

//给SecurityManager注入自定义rememberMe管理器

securityManager.setRememberMeManager(rememberMeManager());

return securityManager;

}

~~~

## 记住我前端标签

因为使用的是thymeleaf模板,比较麻烦些,使用jsp就方便多了

1、pom.xml

~~~xml

<!--html中使用shiro标签-->

<dependency>

<groupId>com.github.theborakompanioni</groupId>

<artifactId>thymeleaf-extras-shiro</artifactId>

<version>2.0.0</version>

</dependency>

~~~

2、shiro配置类中配置方言

~~~java

// 配置shiro方言,thymeleaf页面对shiro标签的支持

@Bean

public ShiroDialect shiroDialect(){

return new ShiroDialect();

}

~~~

3、页面使用

~~~html

<!DOCTYPE html>

<html lang="en" xmlns:th="http://www.thymeleaf.rog" xmlns:shiro="" target="_blank">http://www.pollix.at/thymeleaf/shiro">

<head>

<meta charset="UTF-8">

<title>主页</title>

</head>

<body>

<h1>个人主页</h1>

<p shiro:user="">

Hello, <span shiro:principal=""></span>

</p>

</body>

</html>

~~~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值