zeewane


  • 首页

  • 分类

  • 归档

  • 标签

  • 搜索
close

springmvc注解整理

发表于 2016-10-08   |   分类于 框架   |  

注解

什么是注解?

起步

1
2
<!-- 注解驱动的方式,不用再去写映射器和适配器,也自动导入了json的转换器 -->
<mvc:annotation-driven validator="validator" />

注解整理

Controller

声明这是一个controller,配置了组件扫描之后会自动去找有这个注解的类。

在springmvc的配置文件中加入组件扫描。

1
2
<!-- 组件扫描 -->
<context:component-scan base-package="com.hundsun.ssm.controller" />

RequestMapping

一个url路径。同时里面也可以有参数,可以去限制请求方法。

1
2
// 只能通过post方法来请求
@RequestMapping(value="items",method={RequestMethod.POST})

Autowired

自动装配,有了这个就不用去给每次的注入添加set方法。

RequestBody

请求的格式为json格式。

ajax请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//请求json,输出是json
function requestJson(){
$.ajax({
type:'post',
url:'${pageContext.request.contextPath}/requestJson.action',
contentType:'application/json;charset=utf-8',
//数据格式是json串,商品信息
data:'{"name":"手机","price":999}',
success:function(data){//返回json结果
alert(data);
}
});
}

controller接收处理请求

1
2
3
4
5
6
@RequestMapping("/requestJson")
public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom){
// do something...
return itemsCustom;
}

ResponseBody

响应的格式为json格式。

ajax请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//请求key/value,输出是json
function responseJson(){
$.ajax({
type:'post',
url:'${pageContext.request.contextPath}/responseJson.action',
//请求是key/value这里不需要指定contentType,因为默认就 是key/value类型
//contentType:'application/json;charset=utf-8',
//数据格式是json串,商品信息
data:'name=手机&price=999',
success:function(data){//返回json结果
alert(data.name);
}
});
}

controller接收处理

1
2
3
4
5
6
// 返回的是json,输入的是key/value格式
@RequestMapping("/responseJson")
public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom){
// do something...
return itemsCustom;
}

ModelAttribute

Validated

分组的时候用。

fastjson试用

发表于 2016-09-21   |   分类于 基础   |  

什么是fastjson

Fastjson是一个Java语言编写的高性能功能完善的JSON库。它采用一种“假定有序快速匹配”的算法,把JSON Parse的性能提升到极致,是目前Java语言中最快的JSON库。Fastjson接口简单易用,已经被广泛使用在缓存序列化、协议交互、Web输出、Android客户端等多种应用场景。

简单的小例子

pom依赖

1
2
3
4
5
6
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.17</version>
</dependency>

User.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.hundsun.shiro.JsonTest;
/**
* user实体类
*
* @author zeewane
* @date 2016年9月21日 下午3:40:41
*/
public class User {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

Group.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.hundsun.shiro.JsonTest;
import java.util.List;
import java.util.ArrayList;
/**
* group的实体类
*
* @author zeewane
* @date 2016年9月21日 下午3:42:05
*/
public class Group {
private Long id;
private String name;
private List<User> users = new ArrayList<User>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}

Test1.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.hundsun.shiro.JsonTest;
import com.alibaba.fastjson.JSON;
/**
* fastjson的测试类,java对象和json格式之间的互转
*
* @author zeewane
* @date 2016年9月21日 下午3:47:20
*/
public class Test1 {
public static void main(String[] args) {
Group group = new Group();
group.setId(0L);
group.setName("admin");
User guestUser = new User();
guestUser.setId(2L);
guestUser.setName("guest");
User rootUser = new User();
rootUser.setId(3L);
rootUser.setName("root");
group.getUsers().add(guestUser);
group.getUsers().add(rootUser);
// 将java对象转换成json
String jsonString = JSON.toJSONString(group);
System.out.println(jsonString);
// 将json重新转换为java对象
Group group2 = JSON.parseObject(jsonString, Group.class);
System.out.println(group2.getName());
for (User user : group2.getUsers()) {
System.out.println(user.getId() + ":" + user.getName());
}
}
}
// output
{"id":0,"name":"admin","users":[{"id":2,"name":"guest"},{"id":3,"name":"root"}]}
admin
2:guest
3:root

win10无法新建的解决办法

发表于 2016-09-14   |  

因为显示有病毒,就一时冲动去下载了据说口碑比较好的360 total security,也就是传说中的360国际版。
然后装上以后就给我来了一通扫描,然后说优化,然后就删了我一堆注册表……直接导致少了git、svn、新建和intel显卡驱动的右键操作。
新建的找了好久都找不到办法,后来试了一个,成了。另外的三个预计是要重新安装程序了,刚好把git的版本降低一点。

  1. 首先用管理员身份打开cmd。
  2. 输入命令
    1
    cmd /k reg add "HKEY_CLASSES_ROOT\Directory\Background\shellex\ContextMenuHandlers\New" /ve /t REG_SZ /d {D969A300-E7FF-11d0-A93B-00A0C90F2719} /f

直接运行即可。
新建功能修复,待会来测试git和svn是否正常。

update at 2016.09.18

重新安装git和svn后,右键功能正常,集成显卡的驱动要到时候更新以后才会出现了,不过这个无妨,平时不大用。

sublime text3 注册码

发表于 2016-09-09   |   分类于 tools   |  
1
2
3
4
5
6
7
8
9
10
11
12
13
—– BEGIN LICENSE —–
Michael Barnes
Single User License
EA7E-821385
8A353C41 872A0D5C DF9B2950 AFF6F667
C458EA6D 8EA3C286 98D1D650 131A97AB
AA919AEC EF20E143 B361B1E7 4C8B7F04
B085E65E 2F5F5360 8489D422 FB8FC1AA
93F6323C FD7F7544 3F39C318 D95E6480
FCCC7561 8A4A1741 68FA4223 ADCEDE07
200C25BE DBBC4855 C4CFB774 C5EC138C
0FEC1CEF D9DCECEC D3A5DAD1 01316C36
—— END LICENSE ——

过了这么久依旧可以用……

一个超级酷的网页背景

发表于 2016-09-02   |   分类于 前端   |  
1
2
3
<script src="http://open.sojson.com/common/js/canvas-nest.min.js"
count="200" zindex="-2" opacity="0.5" color="47,135,193" type="text/javascript">
</script>

超级酷的一个效果,在sojson网站上看到的,站长分享了。
会根据鼠标的位置,然后折线不断的汇聚,最后变成很酷的一个圆。

注意事项
要把这个放在body里面,放在header里会出事。

shiro源码分析博客

发表于 2016-08-31   |   分类于 框架   |  

发现一个超级棒的shiro源码分析博客,大神就是屌。

shiro源码分析(一)入门
shiro源码分析(二)Subject和Session
shiro源码分析(三)授权、认证、缓存的接口设计
shiro源码分析(四)具体的Realm
shiro源码分析(五)CredentialsMatcher
shiro源码分析(六)CredentialsMatcher的案例分析

关于登陆加密的一些疑惑

发表于 2016-08-30   |   分类于 框架   |  

shiro里面预置了一些散列的加密算法,这些加密算法都是要逼近单向散列函数,简单的理解就是你可以通过输入的密码,然后放入哈希算法中去计算得到一串32位的字符串,然后持久化保存到数据库中,但是你没有办法把这个32位的字符串还原为密码,这个加密的过程是不可逆的。所以shiro这边在进行认证的时候,会在认证器Authenticator里写好加密的算法,用户认证的时候传入的token中有用户的密码,提取出来然后经过运算得到这串32位的字符串,去和数据库中的进行比较,如果一致就认证通过。这个操作避免了明文储存密码的隐患,数据库被入侵也不会导致用户的密码泄露。

因为一些简单密码的存在,所以通过md5的密文来反向推导出那些简单的密码也是可行的,这类网站也大量的存在。所以就有了盐这个概念,用户的密码加上盐然后再进行散列计算,这串密文被反向破解的几率就大大减小,每个用户都有自己的随机生成的盐,被保存在数据库中。这个操作是避免了数据库中简单密码被破解的隐患。

但是如果请求在中途被拦截,这时候用户的密码万一是明文传输,就被直接被拦住然后解析出来了,下次就直接拿着这个用户名和密码去登陆就行。所以要在客户端就进行加密,盐可以在前端就随机生成然后保存在session中,和密文一并传给server。服务器从session中获取盐,进行运算和比较,相同就通过认证。这个操作避免了请求被拦截后密码直接被取出的问题。(这个是我自己瞎想的,感觉逻辑有问题,这个session中的盐有什么用,数据库中要存的是密文,传过来的盐不是一点价值都没有,又不可能数据库里取出来然后解密再加密。所以应该是客户端传过来的的确应该是密文,但是收到以后要进行解密,变成用户输入的密码原文后再进行从数据库中读取盐,进行加密运算,然后再和数据库中保存的密文比较,相同就通过),所以客户端的盐是功能是,防止简单密码被拦截然后破解。

但是,如果是在用户点击登录按钮的瞬间,获取了password输入框里面的value值呢?他们不需要知道加密算法和盐是什么东西,他们只需要拿到用户的账号和密码就可以在下次直接登陆,那这个东西不是没办法去防范了,除非借助手机验证码这种可以证明身份的形式,或者是大数据进行分析,得出常用登陆电脑和登陆地点进行判断,不是常用的设备和地点就发验证码,是就可以登陆,不然没办法阻止他们获取用户名和密码啊。

是不是要换种思路,我理解的只有散列算法,太狭隘了?还是说道高一尺魔高一丈,是不可能完全阻止密码被获取的,动态的验证码也只能避免他们去成功登陆,并不能阻止他们获取正确的密码。

证书

update at 2016/09/27 14:10

需要一个签名(证书),而且一定要有时效性,时间戳是不可见的,随机数让他看到就看到,证书包括的是时间戳、密文、状态位。

首先传过来的肯定是要一个密文的形式,不能明文传输,需要加密后进行传输。

在用户请求登陆页面的时候,服务器同步生成一个签名,保存在服务器,并随着登陆页面返回过去,放在隐藏输入框中,签名的作用是判断用户请求的表单是不是服务器响应返回的那个,辨别被挟持伪造的请求,具有时效性。里面存着:时间戳(用户请求登陆的时间),状态位(判断是否被劫持伪造过),随机生成的密文,作为判断。

用户接收到登陆页面开始登陆,输入用户名、密码,点击登陆按钮开始登陆,此时对明文密码进行加密然后进行传输。此时签名也作为隐藏的输入域被提交, 状态位此时被修改,(状态位不应该这么使用,这样被拦截了也没什么用啊),同时提交的还有当前的时间戳。

服务端接收到用户登录的信息后,首先要去进行签名的对照,两个时间之间的间隔是否超时,若超时则判断此登陆无效;此外判断签名中的密文是否一致,状态位是否正确,如果通过,就进入密码核对环节。

数据库进行散列存储是为了避免内部运维看到明文密码,或者数据库泄露被拖库后导致用户信息流出。这一步在注册的时候即完成,随机生成一个盐,保存在用户的身份信息表中,不可逆的散列算法计算多次后保存密码。

再加上判断是不是常用地点、常用设备登陆,不是就加上一个手机短信验证码的程序,来判断是不是本人在使用。

不可能做到绝对的安全。

shiro授权代码流程

发表于 2016-08-26   |   分类于 框架   |  

授权流程

  1. shiro拦截到需要权限的url的时候,就传入需要的permission,进行subject.isPermitted(permission)的检查。
  2. DelegatingSubject代理类接收到permission后,调用自己的isPermitted方法进行判断。判断分为两步:hasPrincipals && securityManager.isPermitted(getPrincipals(),permission)。首先是判断用户的身份信息是否存在切有效,第二步是判断用户的凭证信息和用户所要请求的权限是否符合。
  3. AuthorizingSecurityManager调用this.authorizer.isPermitted(principals,permission)进行判断,返回一个布尔值。authorizer在AuthorizingSecurityManager的构造函数中就被new,就是ModularRealmAuthorizer。
  4. ModularRealmAuthorizer接收到参数后,遍历realm,进行权限判断。((Authorizer) realm).isPermitted(principals, permission)。
  5. 上面的isPermitted方法是定义在AuthorizingRealm中的,自定义的realm都要继承这个类,也就是说自定义的realm都有这个判断的方法。realm内部首先会拿着principals去获取到用户的权限信息AuthorizationInfo,然后对权限信息进行遍历查找,看看permission是否存在于info中,如果存在就返回true。

AuthorizationTest

传入权限标识符,返回一个布尔值,表示是否有这个权限。

1
2
// isPermitted里面传入的是权限标识符
subject.isPermitted("user:create:1");

DelegatingSubject

权限标识符传入后,进行两个判断,一个是当前是否存在这个身份信息,另外一个是这个用户是否有这个权限,两个都是true才返回true,权限认证通过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// String类型的权限标识符
public boolean isPermitted(String permission) {
return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission);
}
// 权限
public boolean isPermitted(Permission permission) {
return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission);
}
// 是否有用户凭证信息,也就是用户是否存在的意思
protected boolean hasPrincipals() {
return !CollectionUtils.isEmpty(getPrincipals());
}
// 返回主身份信息吗?只返回了集合中的第一个
public PrincipalCollection getPrincipals() {
List<PrincipalCollection> runAsPrincipals = getRunAsPrincipalsStack();
return CollectionUtils.isEmpty(runAsPrincipals) ? this.principals : runAsPrincipals.get(0);
}
// 从session中获取用户的身份凭证信息
@SuppressWarnings("unchecked")
private List<PrincipalCollection> getRunAsPrincipalsStack() {
Session session = getSession(false);
if (session != null) {
return (List<PrincipalCollection>) session.getAttribute(RUN_AS_PRINCIPALS_SESSION_KEY);
}
return null;
}

AuthorizingSecurityManager

调用ModularRealmAuthorizer中的权限判断方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private Authorizer authorizer;
// 构造函数中就new了一个ModularRealmAuthorizer
public AuthorizingSecurityManager() {
super();
this.authorizer = new ModularRealmAuthorizer();
}
public boolean isPermitted(PrincipalCollection principals, String permissionString) {
return this.authorizer.isPermitted(principals, permissionString);
}
public boolean isPermitted(PrincipalCollection principals, Permission permission) {
return this.authorizer.isPermitted(principals, permission);
}

ModularRealmAuthorizer

遍历realm集合,只要有一个realm判断认证通过,就返回true,这边的realm可以是自己设置进去的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public boolean isPermitted(PrincipalCollection principals, String permission) {
assertRealmsConfigured();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)) continue;
if (((Authorizer) realm).isPermitted(principals, permission)) {
return true;
}
}
return false;
}
public boolean isPermitted(PrincipalCollection principals, Permission permission) {
assertRealmsConfigured();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)) continue;
// 调用realm来进行授权的查询,只要一个满足就返回true
if (((Authorizer) realm).isPermitted(principals, permission)) {
return true;
}
}
return false;
}

AuthorizingRealm

这是realm内部进行权限查询的流程,要求的自定义的realm全部都要继承它。会把用户的身份信息传入进去,然后获取到用户的授权信息,AuthorizerInfo,然后遍历info中的权限信息,只要存在,就返回true。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public boolean isPermitted(PrincipalCollection principals, String permission) {
Permission p = getPermissionResolver().resolvePermission(permission);
return isPermitted(principals, p);
}
public boolean isPermitted(PrincipalCollection principals, Permission permission) {
// 这就是在自定义的realm里override的方法
AuthorizationInfo info = getAuthorizationInfo(principals);
// 会从info中获取信息
return isPermitted(permission, info);
}
private boolean isPermitted(Permission permission, AuthorizationInfo info) {
// 取出所有的权限信息然后遍历寻找
Collection<Permission> perms = getPermissions(info);
if (perms != null && !perms.isEmpty()) {
for (Permission perm : perms) {
if (perm.implies(permission)) {
return true;
}
}
}
return false;
}

shiro认证代码流程

发表于 2016-08-25   |   分类于 框架   |  

认证流程

  1. subject提交认证,传过去token参数
  2. DelegatingSubject代理类调用securityManager.login(this,token),此方法会通过token查询,获取到一个subject,接下来会对这个subject进行检查,如果没有问题,就把subject的内容塞入到this中,也就是当前调用认证方法的那个subject中,完成副作用。
  3. DefaultSecurityManager的login方法拿到subject和token参数后,调用父类的方法:authenticate(token)来获取到一个认证信息AuthenticationInfo。接下来会调用createSubject方法,把AuthenticationInfo、token和subject重新组装成一个新的subject,返回。
  4. AuthenticatingSecurityManager方法中的authenticate(token)方法接收到token后,执行操作,return this.authenticator.authenticate(token)。authenticator在new的时候就自动创建完成,为ModularAuthenticator,返回一个认证信息AuthenticationInfo。
  5. AbstractAuthenticator方法中会有一个方法info = doAuthenticate(token),来获取认证信息,ModularAuthenticator方法中写了这个。
  6. ModularAuthenticator方法继承了抽象类AbstractAuthenticator,拿到token后去realm里查询用户的认证信息,自定义的realm就在这里的集合中,通过注入的方式加入集合,也可以配置认证的三个策略。自定义的realm覆盖重写doGetAuthenticationInfo方法。

subject.login(token)

1
2
3
4
5
6
7
// 执行认证的提交
try {
subject.login(token);
} catch (AuthenticationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

DelegatingSubject

subject的代理类。

1
2
3
public class DelegatingSubject implements Subject {
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public void login(AuthenticationToken token) throws AuthenticationException {
clearRunAsIdentitiesInternal();
// subject是返回的已经认证处理过的,根据token去查询出来的subject对象
Subject subject = securityManager.login(this, token);
// 用户的身份凭证信息
PrincipalCollection principals;
String host = null;
// 把subject中获取到的凭证信息放进去
if (subject instanceof DelegatingSubject) {
DelegatingSubject delegating = (DelegatingSubject) subject;
//we have to do this in case there are assumed identities - we don't want to lose the 'real' principals:
principals = delegating.principals;
host = delegating.host;
} else {
principals = subject.getPrincipals();
}
// 如果凭证信息为空,就是没有有效的用户认证信息返回,就抛出异常
if (principals == null || principals.isEmpty()) {
String msg = "Principals returned from securityManager.login( token ) returned a null or " +
"empty value. This value must be non null and populated with one or more elements.";
throw new IllegalStateException(msg);
}
// 有合法的凭证信息,就全部塞到调用login的那个subject中,完成副作用
this.principals = principals;
this.authenticated = true;
if (token instanceof HostAuthenticationToken) {
host = ((HostAuthenticationToken) token).getHost();
}
if (host != null) {
this.host = host;
}
Session session = subject.getSession(false);
if (session != null) {
this.session = decorate(session);
} else {
this.session = null;
}
}

DefaultSecurityManager

这是一个默认的安全管理器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info;
try {
// 根据token去进行认证,返回一个认证信息,authenticationInfo
info = authenticate(token);
} catch (AuthenticationException ae) {
try {
// 认证失败就抛出认证失败的异常
onFailedLogin(token, ae, subject);
} catch (Exception e) {
if (log.isInfoEnabled()) {
log.info("onFailedLogin method threw an " +
"exception. Logging and propagating original AuthenticationException.", e);
}
}
throw ae; //propagate
}
// 如果到这步,说明认证成功,把认证信息、令牌等全部组装成一个新的subject,返回这个已经通过认证的,已登录的subject
Subject loggedIn = createSubject(token, info, subject);
onSuccessfulLogin(token, info, loggedIn);
return loggedIn;
}

AuthenticatingSecurityManager

1
2
3
4
public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
// 值得一提的是这个authenticator是默认的new的一个ModularRealmAuthenticator
return this.authenticator.authenticate(token);
}

AbstractAuthenticator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
if (token == null) {
throw new IllegalArgumentException("Method argumet (authentication token) cannot be null.");
}
log.trace("Authentication attempt received for token [{}]", token);
AuthenticationInfo info;
try {
// 进行认证,认证信息放到info中,否则抛异常
info = doAuthenticate(token);
if (info == null) {
String msg = "No account information found for authentication token [" + token + "] by this " +
"Authenticator instance. Please check that it is configured correctly.";
throw new AuthenticationException(msg);
}
} catch (Throwable t) {
AuthenticationException ae = null;
if (t instanceof AuthenticationException) {
ae = (AuthenticationException) t;
}
if (ae == null) {
//Exception thrown was not an expected AuthenticationException. Therefore it is probably a little more
//severe or unexpected. So, wrap in an AuthenticationException, log to warn, and propagate:
String msg = "Authentication failed for token submission [" + token + "]. Possible unexpected " +
"error? (Typical or expected login exceptions should extend from AuthenticationException).";
ae = new AuthenticationException(msg, t);
}
try {
notifyFailure(token, ae);
} catch (Throwable t2) {
if (log.isWarnEnabled()) {
String msg = "Unable to send notification for failed authentication attempt - listener error?. " +
"Please check your AuthenticationListener implementation(s). Logging sending exception " +
"and propagating original AuthenticationException instead...";
log.warn(msg, t2);
}
}
throw ae;
}
log.debug("Authentication successful for token [{}]. Returned account [{}]", token, info);
notifySuccess(token, info);
return info;
}

ModularRealmAuthenticator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured();
Collection<Realm> realms = getRealms();
// 判断多个realm还是单个realm
if (realms.size() == 1) {
return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
} else {
return doMultiRealmAuthentication(realms, authenticationToken);
}
}
// 单个realm进行认证
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
if (!realm.supports(token)) {
String msg = "Realm [" + realm + "] does not support authentication token [" +
token + "]. Please ensure that the appropriate Realm implementation is " +
"configured correctly or that the realm accepts AuthenticationTokens of this type.";
throw new UnsupportedTokenException(msg);
}
// 这个get方法就是我们自定义realm的时候override的方法
AuthenticationInfo info = realm.getAuthenticationInfo(token);
if (info == null) {
String msg = "Realm [" + realm + "] was unable to find account data for the " +
"submitted AuthenticationToken [" + token + "].";
throw new UnknownAccountException(msg);
}
return info;
}

注意的是,ModularRealmAuthenticator中的realm,是有set方法的,在spring中直接通过配置文件注入的方式set进来即可。

多个realm的配置方法

其中也有三种不同的realm认证策略,基本上都是针对多个realm配置的情况。

1
2
3
4
5
6
// 配置认证策略
DefaultSecurityManager securityManager = new DefaultSecurityManager();
ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
authenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
securityManager.setAuthenticator(authenticator);

ini配置方法

1
2
3
4
5
6
[main]
#authenticator
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
authenticationStrategy=org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy
authenticator.authenticationStrategy=$authenticationStrategy
securityManager.authenticator=$authenticator

spring注入的方法

1
2
3
4
<!-- 配置认证策略 -->
<property name="authenticator.authenticationStrategy">
<bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy" />
</property>

hexo的4000端口被占用的解决办法

发表于 2016-08-22   |  

前几天发现无法访问hexo原来的本地地址,localhost:4000一直在转啊转,进不去,猜测是端口被占用。
修改端口的方法是,node_modules\hexo-server里的index.js文件,

1
2
3
4
5
6
7
8
9
10
11
12
13
/* global hexo */
'use strict';
var assign = require('object-assign');
hexo.config.server = assign({
port: 4001,
log: false,
ip: '0.0.0.0',
compress: false,
header: true
}, hexo.config.server);

修改port即可。

1234
戚志伟

戚志伟

To be a better man.

36 日志
9 分类
29 标签
RSS
知乎 微博 GitHub
© 2016 - 2017 戚志伟
由 Hexo 强力驱动
主题 - NexT.Pisces