您的位置:首页 > 编程语言 > Java开发

Spring security 自定义登录与权限控制

2016-11-18 15:12 525 查看
一、先说必要的配置文件:

1、web.xml文件添加上

<!-- Spring Security 权限框架 -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


2、application-context.xml配置文件

在application-context配置文件中加入spring security配置文件的引用:

<?xml version="1.0" encoding="UTF-8"?>
<beans ......>
<bean>
<!-- Connection Info --> ......此处省略
</bean>
   <bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
......
<!-- if you want to use the Spring Velocity macros, set this property to true -->
<property name="exposeSpringMacroHelpers" value="true"/>
<property name="contentType" value="text/html;charset=UTF-8"></property>

<property name="toolboxConfigLocation">
<value>/WEB-INF/toolbox.xml</value>
</property>
</bean>
<import resource="/securityConfig.xml"/></beans>


3、securityConfig.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd"> 
<!--use-expressions="true" 的意思是开启表达式 , access-denied-page的意思是,当验证权限失败后会跳转到的页面 -->
<security:http auto-config="true" use-expressions="true" access-denied-page="/denied">

<!-- 对所有的资源,进行权限的设置访问-->
      <!-- 所有人都可以访问资源文件 -->
<security:intercept-url pattern="/login" access="permitAll" />
<security:intercept-url pattern="/logout" access="permitAll" />
<security:intercept-url pattern="/slr/prd/**" access="hasAnyRole('SHOW_PRD','ADD_PRD','MOD_PRD','DEL_PRD')" />
<security:intercept-url pattern="/slr/order/**" access="hasAnyRole('SHOW_ORD','CONFIRM_ORD','CANCEL_ORD')" />
<security:intercept-url pattern="/slr/mgm/**" access="hasAnyRole('SHOW_ACCT','ADD_ACCT','MOD_ACCT','DEL_ACCT')" />
<!-- 配置登录页面为login ,登录成功默认跳转到welcome -->
<security:form-login login-page="/login" default-target-url="/welcome"/>
<security:session-management session-authentication-strategy-ref="concurrentSessionControlStrategy" />
</security:http>
   <!--<!– 阻止多端登录 –>-->
<bean id="concurrentSessionControlStrategy" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<property name="maximumSessions" value="1"></property>
</bean>

<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />

<!-- 配置一个认证管理器 -->
<security:authentication-manager alias="authenticationManager">
<!-- 使用自定义的UserDetailService -->
<security:authentication-provider user-service-ref="userDetailsService">
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>

<!--<!– 对密码进行MD5编码 –>-->
<bean class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" id="passwordEncoder"/>

<!--  通过 userDetailsService,Spring会自动的定义用户的访问级别.   -->
<bean id="userDetailsService" class="com.security.UserDetailsServiceImpl" />
</beans>



4、toolbox.xml文件:

<toolbox>
<data type="string">
<key>version</key>
<value>1.3</value>
</data>
......
<tool>
<key>sec</key>
<scope>request</scope>
<class>com.security.SecurityVelocity</class>
</tool>
</toolbox>


这样在vm界面中就可以直接使用啦~

#if($sec.hasAnyRole("SHOW_PRD","ADD_PRD","MOD_PRD","DEL_PRD"))
<h2>商品管理</h2>
<ul>
<li><a href="/slr/prd/list">商品管理</a></li>
</ul>
#end


二、配置文件中的hasAnyRole()方法和UserDetailService的实现

1.SecurityVelocity.java:

public class SecurityVelocity {

public boolean hasAnyRole(String ...roles) {
return isRole(roles);
}

/***
* 前端传入数组参数
* @param viewRole 可变数组 1个或者多个
* @return 是否有权限
*/
private boolean isRole(String ...roles){
/** 前端数组为空 */
if(null == roles || roles.length <= 0){
return false;
}
/** 获取当前用户登录对象 */
UserDetails userDetails = null;
try {
userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
} catch (Exception e) {
return false;
}
if(null == userDetails){
return false;
}
/** 获取当前用户登录对象所有权限 */
Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
if(null == authorities){
return false;
}
boolean flag = false;
Iterator<? extends GrantedAuthority> iter = authorities.iterator();
while (iter.hasNext()) {
GrantedAuthority grantedAuthority = iter.next();
/**遍历前端列表的所有角色*/
for (String vr : roles) {
if(vr.equals(grantedAuthority.getAuthority())){
flag = true;
break;
}
}
if (flag) break; // 已经匹配上角色了则不再需要匹配其它角色。
}
return flag;
}
}


2.UserDetailServiceImpl.java

public class UserDetailsServiceImpl implements UserDetailsService {

@Autowired
private UserAuthService userAuthService;

@Autowired
private UserService userService;

public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {

User securityUser = null;

UserVo mngVo = userService.getSellerByMobile(userName);
//权限设置
Collection<GrantedAuthority> grantedAuthorities = this.getGrantedAuthorities(mngVo);

boolean enables = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
securityUser = new User(mngVo.getId().toString(), mngVo.getPassword(),
enables, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuthorities);
return securityUser;

}

/**
* 根据用户获取该用户拥有的权限
* @param user 当前用户
* @return 用户角色集合
*/
private Set<GrantedAuthority> getGrantedAuthorities(SellerVo user) {
Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();
if(null != user){
List<AuthorityVo> auths = userAuthService.getUserRoleListByUserId(user.getId());
if(null != auths && auths.size() > 0 ) {
for(AuthorityVo auth : auths) {
grantedAuthorities.add(new SimpleGrantedAuthority(auth.getCode()+""));
}
}
}
return grantedAuthorities;
}

}


这个类主要是在登录时调用来设置用户权限信息的。

三:自己登录逻辑与springsecurity结合

public class LoginController extends BaseController {
......
// show login page
@RequestMapping("login")
public String index(Model model, HttpServletRequest request, HttpServletResponse response) {
// 判断登录状态是否还有效,有效的话自动登录
final UserVo  Vo = getLoginUser(request);
       if (userVo != null && userVo.getId() != null) {
//登录信息有效需要将权限信息放入SecurityContextHolder的上下文信息中心,否则可能会造成循环重定向,服务器报错。
Authentication authentication = (Authentication) request.getAttribute(CommonConstants.USER_AUTH);
SecurityContextHolder.getContext().setAuthentication(authentication);
// redirect to homepage
return "redirect:/";
}
	//返回登录页
return LOGIN;
}

// do login action
@RequestMapping("do_login")
public String doLogin(Model model, HttpServletResponse response, HttpServletRequest request,
String mobile, String password) {
log.info("User input data: mobile = " + mobile);
if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
model.addAttribute("msg", "请输入手机号和密码!");
return INDEX;
}
......

try {
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(mobile, password);
	 //调用loadUserByUsername设置权限信息
Authentication authentication = authenticationManager.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
log.info("恭喜用户  " + mobile() + " 登录成功。");
......	
	 response.sendRedirect('上一次访问的页面或welcome');
} catch (IOException ex) {
log.warn("Error happened");
}
return INDEX;


四、登录页login.vm

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页</title>
......</head><body> <div> <p>登录</p> </div> <div> <div> <form id="loginForm" name="loginForm" action="/do_login" METHOD="post" > <h2><!-- a href="$!{base}/register">免费注册</a -->商家登录</h2> <p id="y_lg_02">$!{msg}</p> <div><input type="text" placeholder="手机号" value="$!{mobile}"/></div> <div><input type="password" placeholder="密码" value="$!{password}"/></div> <p><input type="checkbox"/><em>自动登录</em></p> <input type="submit" value="立即登录"/> </form> </div> </div></body></html>


五、注意点:

用spring security默认的登录,登录页的form的action="/manage/j_spring_security_check"这样写,默认会调用UsernamePasswordAuthenticationFilter中的验证信息。

自己写的登录逻辑主要是要把验证信息写入到spring security的上下文中,三行主要的代码如下:

UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(mobile, password);

//调用loadUserByUsername设置权限信息

Authentication authentication = authenticationManager.authenticate(authRequest);

SecurityContextHolder.getContext().setAuthentication(authentication);

对于自动登录的,需要把权限也放入spring security上下文中,但是生成 UsernamePasswordAuthenticationToken对象需要密码的明文,这里我是把它放在缓存中,是自动登录的直接从缓存中取出来放进spring security上下文中。

final UserVo Vo = getLoginUser(request);

if (userVo != null && userVo.getId() != null) {

//登录信息有效需要将权限信息放入SecurityContextHolder的上下文信息中心,否则可能会造成循环重定向,服务器报错。

Authentication authentication = (Authentication) request.getAttribute(CommonConstants.USER_AUTH);

SecurityContextHolder.getContext().setAuthentication(authentication);

// redirect to homepage

return "redirect:/";

}

自动登录开始把权限也放入spring security上下文中,结果出现了循环重定向,是因为之前选择了自动登录,则if条件为true,这时重定向的/login,又开始执行登录页面的代码,造成了重定向死循环。

看到别人写的一篇好文,有助于理解:http://blog.csdn.net/zy_cookie/article/details/49535413
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息