Skip to content

Commit

Permalink
添加注释
Browse files Browse the repository at this point in the history
  • Loading branch information
nineya committed Feb 16, 2021
1 parent 9fe53bd commit 1c7fbe4
Show file tree
Hide file tree
Showing 12 changed files with 90 additions and 91 deletions.
15 changes: 15 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.nineya.study</groupId>
<artifactId>framework-study</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>shiro-study</module>
</modules>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
/**
* @author 殇雪话诀别
* 2021/2/15
* 程序入口
*/
@SpringBootApplication
public class ShiroApplication {
public static void main(String[] args) {
SpringApplication.run(ShiroApplication.class);
}

}
Original file line number Diff line number Diff line change
@@ -1,90 +1,80 @@
package com.nineya.shiro.config;

import com.nineya.shiro.filter.TokenFilter;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.config.ShiroAnnotationProcessorConfiguration;
import org.apache.shiro.spring.config.ShiroBeanConfiguration;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.*;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.bind.annotation.ModelAttribute;

import javax.servlet.Filter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
* @author 殇雪话诀别
* 2021/2/15
* 配置类
*/
@Configuration
public class ShiroConfiguration {

/**
* 配置代理,没有配置将会导致注解不生效
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}

/**
* 配置代理,没有配置将会导致注解不生效
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}

//将自己的验证方式加入容器
/**
* 将自己的验证方式加入容器
* @return
*/
@Bean
public Realm myShiroRealm() {
StudyRealm customRealm = new StudyRealm();
return customRealm;
public Realm studyRealm() {
StudyRealm studyRealm = new StudyRealm();
return studyRealm;
}

// @Bean
// public ShiroFilterChainDefinition shiroFilterChainDefinition() {
// DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
//
// // 其实等同于注解的权限控制
// chainDefinition.addPathDefinition("/admin", "authc, roles[read,write]");
// chainDefinition.addPathDefinition("/index", "authc, perms[select]");
// // 不允许管理员的create权限
// chainDefinition.addPathDefinition("/add", "authc, perms[user:create]");
//
// // 对所有用户认证
// chainDefinition.addPathDefinition("/**", "authc");
//
// return chainDefinition;
// }

// @Bean
// public TokenFilter tokenFilter() {
// return new TokenFilter();
// }
/**
* 不应该将过滤器的实现注册为bean,否则会导致Filter过滤器顺序混乱,导致抛出异常
* 如果一定要注册为 Bean,可以使用 Order 指定优先级,还未尝试过
* @return
*/
public TokenFilter tokenFilter() {
return new TokenFilter();
}

//Filter工厂,设置对应的过滤条件和跳转条件
/**
* Filter工厂,设置对应的过滤条件和跳转条件
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());
Map<String, String> map = new HashMap<>();
//登出
map.put("/logout", "logout");
//对所有用户认证
// 使用我们自己创建的jwt过滤器名称
map.put("/**", "jwt");
//登录
shiroFilterFactoryBean.setLoginUrl("/login");
Expand All @@ -93,39 +83,19 @@ public ShiroFilterFactoryBean shiroFilterFactoryBean() {
//错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
shiroFilterFactoryBean.setFilters(new HashMap<String, Filter>(){{put("jwt", new TokenFilter());}});
shiroFilterFactoryBean.setFilters(new HashMap<String, Filter>(){{put("jwt", tokenFilter());}});
return shiroFilterFactoryBean;
}

//权限管理,配置主要是Realm的管理认证
/**
* 权限管理,配置主要是Realm的管理认证,同时可以配置缓存管理等
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager webSecurityManager = new DefaultWebSecurityManager();

//session管理
// webSecurityManager.setSessionManager(sessionManager());

//realm管理
webSecurityManager.setRealm(myShiroRealm());

//缓存管理
// webSecurityManager.setCacheManager(new MemoryConstrainedCacheManager());
//使用ehcache
// EhCacheManager ehCacheManager = new EhCacheManager();
// ehCacheManager.setCacheManager(getEhCacheManager());
// webSecurityManager.setCacheManager(ehCacheManager);

//redis实现
// webSecurityManager.setCacheManager(redisCacheManager());

// //关闭session
// DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
// DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
// defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
// subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
// webSecurityManager.setSubjectDAO(subjectDAO);
SecurityUtils.setSecurityManager(webSecurityManager);

webSecurityManager.setRealm(studyRealm());
return webSecurityManager;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.util.stream.Collectors;

/**
* 自定义 realm
*
* @author 殇雪话诀别
* 2021/2/15
*/
Expand All @@ -33,7 +35,7 @@ public boolean supports(AuthenticationToken token) {
}

/**
* 授权,在认证之后仔细
* 授权,在认证之后执行
* @param principals
* @return
*/
Expand Down Expand Up @@ -70,7 +72,7 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
if (user == null) {
return null;
}
// 第一个参数是主体,将会在授权时封装成PrincipalCollection进行使用,所以必须将jwt内容传回
// 第一个参数是主体,将会在授权时封装成PrincipalCollection.getPrimaryPrincipal()进行使用,所以必须将jwt内容传回
// 第二个参数是认证信息,即密码,为后面验证可以通过,需要和token中的内容一样
// 第三个参数是领域名称
return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), user.getUserName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@
/**
* @author 殇雪话诀别
* 2021/2/15
* 对未通过权限认证的部分异常进行异常处理
*/
@ControllerAdvice
public class ExceptionController {

public static Subject subject;
@ExceptionHandler
@ResponseBody
public String ErrorHandler(AuthorizationException e) {
System.out.println(subject.getPrincipals());
return "没有通过权限验证!\n" + e.getMessage();
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
package com.nineya.shiro.controller;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.nineya.shiro.entity.User;
import com.nineya.shiro.service.LoginService;
import com.nineya.shiro.util.UserTokenUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.authz.annotation.RequiresUser;
import org.apache.shiro.subject.Subject;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -34,6 +24,16 @@ public class LoginController {
@Resource
private UserTokenUtil tokenUtil;

/**
* 使用 jwt 进行登录时此处逻辑将有些不同
* 如果没有使用Token,用户将在此方法中通过 subject.login(usernamePasswordToken) 进行登录。
* 使用 jwt 时,将不再使用 session 存储登录状态,subject.login(usernamePasswordToken) 逻辑将在 Filter 解析 token 时进行,并且
* 每次请求都需要进行 token 解析和登录操作。
* 也就是说认证、授权两个步骤,原本只要登录时进行认证,每次请求进行授权,使用 jwt 后每次请求都需要记性jwt解析、认证和授权三个步骤。
* @param userName 用户名
* @param password 密码
* @return
*/
@GetMapping("/login")
public String login(@RequestParam("userName") String userName, @RequestParam("password") String password) {
if (StringUtils.isEmpty(userName) || StringUtils.isEmpty(password)) {
Expand All @@ -46,6 +46,7 @@ public String login(@RequestParam("userName") String userName, @RequestParam("pa
return tokenUtil.createToken(userName);
}

// 这是没有使用 jwt 时,基于 session 的实现方式
// @GetMapping("/login")
// public String login(User user) {
// if (StringUtils.isEmpty(user.getUserName()) || StringUtils.isEmpty(user.getPassword())) {
Expand All @@ -72,19 +73,31 @@ public String login(@RequestParam("userName") String userName, @RequestParam("pa
// return "login success";
// }

/**
* 允许角色为 read 且为 write 用户访问
* @return
*/
@RequiresRoles({"read", "write"})
@GetMapping("/admin")
public String admin() {
return "admin";
}

/**
* 允许拥有 select 权限的用户访问
* @return
*/
@RequiresPermissions("select")
@GetMapping("/select")
public String select() {
return "select";
}

@RequiresPermissions("user:create")
/**
* 允许拥有 create 权限的用户访问
* @return
*/
@RequiresPermissions("create")
@GetMapping("/create")
public String create() {
return "create";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/**
* @author 殇雪话诀别
* 2021/2/15
* 角色
* 角色,包含权限集合
*/
public class Role {
private long id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/**
* @author 殇雪话诀别
* 2021/2/15
* 用户
* 用户,包含角色集合
*/
public class User {
private long uid;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ protected boolean isAccessAllowed(ServletRequest request, ServletResponse respon
if (isLoginAttempt(request, response)) {
//如果存在,则进入 executeLogin 方法执行登入,检查 token 是否正确
try {
executeLogin(request, response);
return executeLogin(request, response);
} catch (Exception e) {
//token 错误
e.printStackTrace();
Expand All @@ -62,9 +62,7 @@ protected boolean executeLogin(ServletRequest request, ServletResponse response)
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader(AUTHORIZATION_HEADER);
BearerToken jwtToken = new BearerToken(token, request.getRemoteAddr());
ExceptionController.subject = getSubject(request, response);
ExceptionController.subject.login(jwtToken);
ExceptionController.subject = getSubject(request, response);
getSubject(request, response).login(jwtToken);
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
/**
* @author 殇雪话诀别
* 2021/2/15
* 用户登录服务接口
*/
public interface LoginService {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,22 @@
/**
* @author 殇雪话诀别
* 2021/2/15
* 用户登录服务,使用Map<用户名, 用户信息> 的格式存储用户信息进行查询
* 在实际使用中将此处修改为连接数据库查询即可
*/
@Service
public class LoginServiceImpl implements LoginService {
private final Map<String, User> users = new HashMap<>();

public LoginServiceImpl() {
// 定义三个权限
Permissions permissions1 = new Permissions(1, "create");
Permissions permissions2 = new Permissions(2, "delete");
Permissions permissions3 = new Permissions(3, "select");
// 定义两个角色
Role role1 = new Role(1, "read", new HashSet<Permissions>(){{add(permissions3);}});
Role role2 = new Role(1, "write", new HashSet<Permissions>(){{add(permissions1);add(permissions2);}});
// 定义三个用户分别对应两个角色
users.put("observe", new User(1, "observe", "123456", Collections.singleton(role1)));
users.put("admin", new User(1, "admin", "123456", Collections.singleton(role2)));
users.put("user", new User(1, "user", "123456", new HashSet<Role>(){{add(role1); add(role2);}}));
Expand Down
Loading

0 comments on commit 1c7fbe4

Please sign in to comment.