项目源码地址 https://github.com/nieandsun/security
给自己定下一个目标后 —》 你的生活会变得忙碌、充实!!!
1 认证、授权概念
记得上学的时候,我老师在讲两者的概念时做过这样一个比喻:
在你家门外有一群人,他们有的是你的朋友,有的是你的亲戚,有的是穷凶极恶的歹徒…这时候他们都想进入你家里,你肯定不会让他们全部进来,而只会让你的朋友、亲戚等你信得过的人进来,而这个过程就是
认证
。当你的朋友、亲戚等你信的过的人都进入到你家里以后,你的朋友在没经过你允许的情况下肯定不能打开你家的保险箱、或者随便乱翻你的东西,也就是说这些进入你家的人,他们的有些行为必须在你准许的情况下才可以做,而这就是所谓的授权
。
联系一下我们之前开发的整块内容,其实无论是用户名+密码登陆、短信登陆还是社交登陆都是在做认证相关的工作 — 即准不准许用户访问我们的服务器。
2 系统授权应怎样控制 ???
2.1 由谁控制
如下图所示,对于那个删除按钮,公司里的规定是只有一些在公司里级别较高的人才有资格进行操作。我们该如何只对公司里特定的人员开放这个操作权限呢?
(1)肯定不能通过前端是否展示这个按钮、或者让这个按钮能否可点击这样的方式来控制— 原因可参考我在下图中红色字体的描述。 (这一块应该归结于用户体验问题)
(2)正确的做法应该是由后端服务器通过绑定在这个用户上的权限来决定它能否做出相应的操作。
跟着老师的视频学习的时候看到老师单独讲了一下这一块,我觉得有过一些工作经验的人应该都不会有此误解吧。
2.2 怎样控制 — 控制原理简介
控制原理如下图:(不止spring security,其他的技术栈也应该是这样):
即我们的系统里应该由两份信息,
- 一份信息存了哪些url需要哪些权限才能访问
- 另一份信息存了用户究竟有哪些权限
当一个请求过来时,我们的系统会拿着这个请求的URL看一下需要哪些权限才能访问这个URL,然后再看看访问这个URL的用户有没有这个权限(当然也许是先看一下这个用户有哪些权限,然后再看一下这个请求的URL需要哪些权限才能访问) —》由此来断定该用户有没有权限。
3 简单权限控制
对于某些URL,只有某些权限的用户才能访问,这种逻辑直接写死在代码里,从技术实现上来说很简单,但真实项目中应该很少会这样用,但本文仍主要来演示一下具体的代码实现。
(1)首先来看下面这段配置:— 也可以理解为授权
.and()
.authorizeRequests()
//配置不用进行认证校验的url
.antMatchers(
SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,
nrscSecurityProperties.getBrowser().getLoginPage(),
SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX + "/*",
nrscSecurityProperties.getBrowser().getSignUpUrl(),
//session失效默认的跳转地址
nrscSecurityProperties.getBrowser().getSession().getSessionInvalidUrl(),
//获取第三方账号的用户信息的默认url----微信、QQ登陆没找到与本系统的关联关系时用到---此时没登陆
SecurityConstants.DEFAULT_GET_SOCIAL_USERINFO_URL,
//退出登陆默认跳转的url
nrscSecurityProperties.getBrowser().getSignOutUrl(),
"/user/register",
"/js/**"
)
.permitAll()
//指明除了上面不用认证的url外其他请求都需要认证校验
.anyRequest()
.authenticated()
其实这块配置就是我们之前一直在使用的配置,它的作用就是除了.permitAll()
的这些URL之外,任何用户访问其他的url都必须经过认证。有些人说这也是一种授权,后来我想了一下,或许可以这样理解:
即授权任何用户可以在不经过认证的基础上访问.permitAll()的这些url
但是上面的配置并没有与用户产生联系,也就是说并没有规定哪个用户究竟对哪些URL有访问权限,因为只要用户认证成功之后,就可以随意访问我们的系统 —》 证据如下:
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
下面是之前写的构建UserDetails的方法,有兴趣的可以试一下,无论AuthorityUtils.commaSeparatedStringToAuthorityList(“admin”));这条语句里写“admin”还是“123”或者其他,只要用户登陆成功之后,都是可以直接访问到我们系统的任何url的。
private SocialUserDetails buildUser(String userId) {
// 根据用户名查找用户信息
//根据查找到的用户信息判断用户是否被冻结
String password = passwordEncoder.encode("123456");
log.info("数据库密码是:"+password);
return new SocialUser(userId, password,
true, true, true, true,
AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
(2) 创建简单的权限控制 — 只有权限为ADMIN的用户才能访问我们的/user/*接口
- 在配置文件里指定
.authorizeRequests()
//配置不用进行认证校验的url
.antMatchers(
SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,
nrscSecurityProperties.getBrowser().getLoginPage(),
SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX + "/*",
nrscSecurityProperties.getBrowser().getSignUpUrl(),
//session失效默认的跳转地址
nrscSecurityProperties.getBrowser().getSession().getSessionInvalidUrl(),
//获取第三方账号的用户信息的默认url----微信、QQ登陆没找到与本系统的关联关系时用到---此时没登陆
SecurityConstants.DEFAULT_GET_SOCIAL_USERINFO_URL,
//退出登陆默认跳转的url
nrscSecurityProperties.getBrowser().getSignOutUrl(),
"/user/register",
"/js/**"
)
.permitAll()
//指定只有权限为ADMIN的用户才能访问我们的/user/*接口
.antMatchers("/user/*").hasRole("ADMIN")
//指明除了上面不用认证的url外其他请求都需要认证校验
.anyRequest()
.authenticated()
- 构建用户时要指定用户的权限需要有ADMIN权限 — 下篇文章会从源码角度来讲一下为什么下面要写出ROLE_ADMIN
private SocialUserDetails buildUser(String userId) {
// 根据用户名查找用户信息
//根据查找到的用户信息判断用户是否被冻结
String password = passwordEncoder.encode("123456");
log.info("数据库密码是:"+password);
return new SocialUser(userId, password,
true, true, true, true,
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN"));
}
(3)如何按照请求方式(HttpMethod)来指定用户权限
由于现在后端服务基本都是基于restful 风格进行开发的,所以一个/user/1
可能对应的并不是一个方法,有可能是查询请求(GET),也有可能是删除请求(DELETE),我们也可以按照请求方法对用户进行授权,配置如下:
.antMatchers(HttpMethod.GET, "/user/*").hasRole("ADMIN")
即有ADMIN权限的用户仅有权限调用/user/*对应的GET方法。