springboot整合shiro,thymeleaf,mybatis
文章目录
简介
shiro过滤器 anon authc
注意: shiro提供和多个默认的过滤器,我们可以用这些过滤器来配置控制指定url的权限:
配置缩写 | 对应的过滤器 | 功能 |
---|---|---|
anon | AnonymousFilter | 指定url可以匿名访问(访问时不需要认证授权) |
authc | FormAuthenticationFilter | 指定url需要form表单登录,默认会从请求中获取username、password,rememberMe等参数并尝试登录,如果登录不了就会跳转到loginUrl配置的路径。我们也可以用这个过滤器做默认的登录逻辑,但是一般都是我们自己在控制器写登录逻辑的,自己写的话出错返回的信息都可以定制嘛。 |
authcBasic | BasicHttpAuthenticationFilter | 指定url需要basic登录 |
logout | LogoutFilter | 登出过滤器,配置指定url就可以实现退出功能,非常方便 |
noSessionCreation | NoSessionCreationFilter | 禁止创建会话 |
perms | PermissionsAuthorizationFilter | 需要指定权限才能访问 |
port | PortFilter | 需要指定端口才能访问 |
rest | HttpMethodPermissionFilter | 将http请求方法转化成相应的动词来构造一个权限字符串,这个感觉意义不大,有兴趣自己看源码的注释 |
roles | RolesAuthorizationFilter | 需要指定角色才能访问 |
ssl | SslFilter | 需要https请求才能访问 |
user | UserFilter | 需要已登录或“记住我”的用户才能访问 |
准备工作
create table `user`(
`id` int not null AUTO_INCREMENT,
`username` varchar(255) not null,
`password` VARCHAR(255) not null,
`address` varchar(255) DEFAULT null,
PRIMARY KEY (`id`)
) ENGINE= INNODB DEFAULT charset=utf8;
insert into user values(1,'zhangsan','123','北京');
insert into user values(2,'lisi','456','上海');
INSERT INTO USER values(3,'wangerma','789','合肥');
login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="/login" method="post">
<label for="username">用户名</label>
<input id="username" type="text" name="username"> <br/>
<label for="password">密码</label>
<input id="password" type="text" name="password"> <br/>
<input type="submit" value="登录">
</form>
</body>
</html>
1. pom引入资源
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--shrio-sprinboot整合依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.5.3</version>
</dependency>
<!--mysql+mybatis-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.41</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
2. mybatis数据源配置application.yml
spring:
datasource:
url: jdbc:mysql://rm-bp18jitlw9a952i5x3o.mysql.rds.aliyuncs.com:3306/yeb?userSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.jdbc.Driver
username: root
password: Kclkd2013
3. User实体类以及接口
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
private String address;
private String salt;
}
@Mapper
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
//根据用户名查询密码
//判断注册的用户是否重复
@Select("select password from user where username=#{username}")
String getPasswordByUsername(String username);
}
4. 配置Shiro
ShiroConfig
@Configuration
public class ShiroConfig {
// 1.创建shiroFilter, 负责拦截所有请求
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//给filter设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//配置系统的受限资源
Map<String,String> map = new HashMap<>();
// /**所有资源
map.put("/login","anon"); // anon公共资源
map.put("/**","authc"); // authc: 需要认证和授权
//默认认证界面路径
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
// 2.创建安全管理器
@Bean
public DefaultWebSecurityManager getSecurityManager(@Qualifier("getRealm") Realm realm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//给安全管理器设置realm
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
// 3.创建自定义realm
@Bean
public Realm getRealm(){
return new CustomerRealm();
}
}
CustomerRealm
public class CustomerRealm extends AuthorizingRealm {
@Autowired
UserMapper mUserMapper;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//身份认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("=====认证======");
//首先拿到身份信息 username
String principal = (String) authenticationToken.getPrincipal();
//调用数据库,模拟查询操作
if("zhangsan".equals(principal)){
String password = mUserMapper.getPasswordByUsername(principal);
System.out.println("查询到的密码" + password);
return new SimpleAuthenticationInfo(principal,password,this.getName());
}
return null;
}
}
5.登录认证controller实现
@Controller
public class LoginController {
@GetMapping("/login")
public String toLogin(){
return "login";
}
@GetMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
//在shiro的session中登出该用户
subject.logout();
return "redirect:login";
}
@PostMapping("/login")
public String login(String username,String password){
//获取主体对象 | 安全管理器@Bean已经自动注入
Subject subject = SecurityUtils.getSubject();
try {
subject.login(new UsernamePasswordToken(username,password));
return "redirect:/index";
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名错误");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误");
}
return "redirect:/login";
}
}
6. 用户注册实现
注册页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户注册</title>
</head>
<body>
<h1>用户注册</h1>
<form action="/register" method="post">
<label for="username">用户名</label>
<input id="username" type="text" name="username"> <br/>
<label for="password">密码</label>
<input id="password" type="text" name="password"> <br/>
<label for="address">地址</label>
<input id="address" type="text" name="address"> <br/>
<input type="submit" value="立即注册">
</form>
</body>
</html>
user接口
@Mapper
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
//删除一个用户
@Delete("delete from user where id = #{id}")
void deleteUserById(Integer id);
//根据用户名查询密码
@Select("select password from user where username=#{username}")
String getPasswordByUsername(String username);
//注册,添加一个用户
@Insert("insert into user(username,password,address,salt) values(#{username},#{password},#{address},#{salt})")
void addUser(User user);
//根据用户名,查看是否存该用户
@Select("select count(*) from user where username=#{username}")
Integer getUserCountByName(String username);
//根据用户名查询,返回一个对象
@Select("select id,username,password,salt from user where username=#{username}")
User getByName(String username);
}
用户service层
@Service
@Transactional
public class UserService {
@Autowired
UserMapper mUserMapper;
//添加一个用户
public void addUser(User user){
//生成随机盐
String salt = SaltUtils.getSalt(8);
user.setSalt(salt);
//明文密码进行md5+salt+hash散列
Md5Hash md5Hash = new Md5Hash(user.getPassword(), salt, 1024);
user.setPassword(md5Hash.toHex());
mUserMapper.addUser(user);
}
//根据用户名,查看是否存该用户
public Integer getUserCountByName(String username){
return mUserMapper.getUserCountByName(username);
}
public List<User> getUsers(){
return mUserMapper.getUsers();
}
//根据用户名查询密码
public String getPasswordByUsername(String username){
return mUserMapper.getPasswordByUsername(username);
}
//删除一个用户
public void deleteUserById(Integer id){
mUserMapper.deleteUserById(id);
}
//根据用户名查询,返回一个对象
public User getByName(String username){
return mUserMapper.getByName(username);
}
}
Register Controller 控制层
@Controller
public class RegisterController {
@Autowired
UserService mUserService;
@GetMapping("/register")
public String toReg(){
return "register";
}
@PostMapping("/register")
public String register(User user){
//查看是否已经存在该用户
if(mUserService.getUserCountByName(user.getUsername()) != 0){
System.out.println("用户名已经存在!");
return "redirect:/register";
}
mUserService.addUser(user);
System.out.println("已添加完成该用户");
return "redirect:/login";
}
}
7.用户登录实现(md5+salt+hash)
CustomerRealm
public class CustomerRealm extends AuthorizingRealm {
@Autowired
UserService mUserService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//身份认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("=====认证======");
//首先拿到身份信息 username
String principal = (String) authenticationToken.getPrincipal();
//查询有该用户是否存在
boolean isExistUser;
if(mUserService.getUserCountByName(principal) > 0){
isExistUser = true;
}
//调用数据库,模拟查询操作
if(true){
User user = mUserService.getByName(principal);
//查询密码
String sqlPassword = user.getPassword();
//查询随机盐
String sqlSalt = user.getSalt();
System.out.println("查询到的密码" + sqlPassword);
System.out.println("查询到的随机盐" + sqlSalt);
/**
*
* 用户输入的password不需要任何处理
* realm需要开启md5,设置,散列次数(HashedCredentialsMatcher)
* 认证信息传入,加密过的密码,随机盐就可以了
* shiro会自动根据用户数据的password进行md5和hash操作,然后再进行匹配
*/
return new SimpleAuthenticationInfo(principal,sqlPassword, ByteSource.Util.bytes(sqlSalt),this.getName());
}
return null;
}
}
盐工具类
public class SaltUtil {
public static String getSalt(int n){
char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
StringBuffer sb = new StringBuffer();
for(int i = 0; i < n; i++){
sb.append(chars[new Random().nextInt(chars.length)]);
}
return sb.toString();
}
}
ShiroConfig配置实现md5+hash
@Configuration
public class ShiroConfig {
// 1.创建shiroFilter, 负责拦截所有请求
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//给filter设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//配置系统的受限资源
Map<String,String> map = new HashMap<>();
// /**所有资源
map.put("/login","anon"); // anon公共资源
map.put("/register","anon"); // anon公共资源
map.put("/public/**","anon"); // anon公共资源
map.put("/**","authc"); // authc: 需要认证和授权
//默认认证界面路径
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
// 2.创建安全管理器
@Bean
public DefaultWebSecurityManager getSecurityManager(@Qualifier("getRealm") Realm realm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//给安全管理器设置realm
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
// 3.创建自定义realm
@Bean
public Realm getRealm(){
CustomerRealm customerRealm = new CustomerRealm();
HashedCredentialsMatcher hcm = new HashedCredentialsMatcher();
hcm.setHashIterations(1024);
hcm.setHashAlgorithmName("md5");
customerRealm.setCredentialsMatcher(hcm);
return customerRealm;
}
}