本文非常非常长,希望大家有足够时间及耐心阅读,直接复制代码跑通程序大概也得半个钟,框架使用简单,配置多
废话不说,直接做,做完就懂了,不懂再做一遍,培养动手能力
一、导入jar包(此处只导入Spring security相关jar包) 本次测试使用SSM框架测试
二、配置web.xml:过滤器最好放在其他过滤器的最前面
<filter>
<filter-name>springSecurityFilterChainfilter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
filter>
<filter-mapping>
<filter-name>springSecurityFilterChainfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
三、创建spring-security.xml(需要在applicationContext.xml文件中导入),
配置文件中多处引用我们自己创建的类,后续有图描述,可以直接下拉到第十二个标题,现在位置是第三个标题
导入语句:<import resource="spring-security.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:context="http://www.springframework.org/schema/context"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.2.xsd ">
<context:component-scan base-package="com.it.security"/>
<sec:global-method-security
secured-annotations="enabled" jsr250-annotations="enabled" pre-post-annotations="enabled"/>
<sec:http pattern="/resources/**" security="none"/>
<sec:http pattern="/verify/**" security="none"/>
<sec:http pattern="/login" security="none"/>
<sec:http pattern="/user/register.*" security="none"/>
<sec:http pattern="/favicon.ico" security="none"/>
<sec:http use-expressions="true" auto-config="true" entry-point-ref="authenticationProcessingFilterEntryPoint">
<sec:headers>
<sec:frame-options policy="SAMEORIGIN"/>
sec:headers>
<sec:intercept-url pattern="/**" access="permitAll"/>
<sec:form-login login-page="/member/login"
username-parameter="loginname"
password-parameter="loginpwd"
login-processing-url="/member/yy"
default-target-url="/member/my"
always-use-default-target="false"
authentication-success-handler-ref="success_handler"
authentication-failure-handler-ref="failure_handler"
/>
<sec:remember-me data-source-ref="dataSource" token-validity-seconds="1209600" remember-me-parameter="remember_me" />
<sec:logout invalidate-session="true" logout-url="/logout" logout-success-url="/member/login"/>
<sec:csrf disabled="true"/>
sec:http>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="daoAuthenticationProvider" />
sec:authentication-manager>
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="myuserdetailservice"/>
<property name="hideUserNotFoundExceptions" value="false" />
<property name="passwordEncoder" ref="encoder2"/>
bean>
<bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg name="loginFormUrl" value="/member/login" />
bean>
<bean id="encoder2"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<constructor-arg name="strength" value="9" />
bean>
beans>
四、为权限控制设计数据表
#用户表
drop table if exists `user`;
create table `user`(
`user_id` int PRIMARY KEY
,`user_nickname` varchar(30) not null
,`user_loginname` varchar(30) not null
,`user_loginpwd` varchar(30) not null
);
#角色表
create table sec_role
(
roleid int auto_increment primary key not null,
rolename varchar(20) not null
);
insert into sec_role values(1,'SuperAdmin');
insert into sec_role values(2,'Admin');
insert into sec_role values(3,'Manager');
insert into sec_role values(4,'Group');
insert into sec_role values(5,'Emp');
#功能表(如查看列表)
create table sec_permission
(
pmsid int auto_increment primary key not null,
pmsname varchar(20),
pmsdesc varchar(20)
);
insert into sec_permission values(1,'checkIn','签到');
insert into sec_permission values(2,'checkOut','签退');
insert into sec_permission values(3,'checkin:list:all','查看考勤列表-全部');
insert into sec_permission values(4,'checkin:list:dept','查看考勤列表-部门');
insert into sec_permission values(5,'checkin:list:my','查看考勤列表-个人');
#用户角色表
create table sec_user_role
(
urid int auto_increment primary key not null,
userid int references users(user_id),
roleid int references sec_role(roleid)
);
insert into sec_user_role values(1,1,2);
insert into sec_user_role values(2,1,5);
#角色权限表
create table sec_role_permission
(
rpid int auto_increment primary key not null,
roleid int references sec_role(roleid),
pmsid int references sec_permission(pmsid)
);
insert into sec_role_permission values(1,2,3);
insert into sec_role_permission values(2,2,4);
insert into sec_role_permission values(3,2,6);
insert into sec_role_permission values(4,2,10);
insert into sec_role_permission values(5,3,4);
insert into sec_role_permission values(6,3,9);
insert into sec_role_permission values(7,5,1);
insert into sec_role_permission values(8,5,2);
insert into sec_role_permission values(9,5,5);
insert into sec_role_permission values(10,5,8);
#额外授权表(添加其他角色拥有的某一功能)
create table sec_user_add_permission
(
upid int auto_increment primary key not null,
userid int references users(user_id),
pmsid int references sec_permission(pmsid)
);
insert into sec_user_add_permission values(1,4,4);
#创建视图的用途是,java中编写select连表查询比较繁琐,直接使用视图查询出所需的功能集合和角色集合
create view v_user_permission as
(select distinct(p.pmsname),u.user_id from users u
join sec_user_role ur on u.user_id=ur.userid
join sec_role r on r.roleid=ur.roleid
join sec_role_permission rp on rp.roleid=r.roleid
join sec_permission p on p.pmsid=rp.pmsid)
union
(select distinct(p.pmsname) as xx,u.user_id from sec_user_add_permission up
join users u on u.user_id=up.userid
join sec_permission p on p.pmsid=up.pmsid);
create view v_user_role as
select r.rolename,u.user_id from users u
join sec_user_role ur on u.user_id=ur.userid
join sec_role r on r.roleid=ur.roleid ;
在Mapper文件中的查询语句
//获取该用户的所有功能
<select id="selectPermissions" resultType="String">
SELECT pmsname FROM v_user_permission WHERE user_id=#{id}
select>
//获取该用户的所有角色
<select id="selectRoles" resultType="String">
SELECT rolename FROM v_user_role WHERE user_id=#{id}
select>
五、创建MyUser类,存储用户类的信息及其他(加强版User类),界面层使用的用户类
import com.it.entity.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
//按 spring security标准制造的用户类
public class MyUser implements UserDetails{
private User user;//User类需要实现序列化
private Set
private Set
Set
public MyUser() {
}
public MyUser(User user,Set
this.user=user;
this.pmsSet = pmsSet;
this.roles = roles;
for(String pms:pmsSet){
authorities.add(new SimpleGrantedAuthority(pms));
}
for(String role:roles){
authorities.add(new SimpleGrantedAuthority("ROLE_"+role));
}
}
//判断是否拥有此功能
public boolean hasAuthority(String str){
for (GrantedAuthority au :this.authorities){
if(au.getAuthority().equalsIgnoreCase(str)){
return true;
}
}
return false;
}
//权限
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
@Override
public String getPassword() {
return this.member.getLoginPwd();
}
@Override
public String getUsername() {
return this.user.getLoginName();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user= user;
}
public Set
return pmsSet;
}
public void setPmsSet(Set
this.pmsSet = pmsSet;
}
public Set
return roles;
}
public void setRoles(Set
this.roles = roles;
}
}
六、创建密码验证类
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
//对比密码使用的类,如使用BCryptPasswordEncoder加密类,此类失效
@Component("mypasswordencoder")
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
//加密
return charSequence.toString();//不加密直接返回
}
@Override
public boolean matches(CharSequence charSequence, String s) {
//可按自己的做法比较密码,如MD5脱盐后再比较
return charSequence.toString().equalsIgnoreCase(s);//对比密码
}
}
七、创建查询用户类
import com.it.entity.Cdt;
import com.it.entity.User;
import com.it.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Set;
//用于从数据库加载用户
@Component("myuserdetailservice")
public class MyUserDetailsService implements UserDetailsService {
@Autowired
@Qualifier("userservice")
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user= userService.findObject(Arrays.asList(new Cdt("user_loginname","=",username)));
if(user== null){throw new UsernameNotFoundException("用户名不存在");}
Set
Set
return new MyUser(user,pmsSet,roles);//将用户,功能集合,角色集合塞进MyUser返回出去
}
}
七、制作SecurityUtil类,方便获取MyUser
import com.it.security.MyUser;
import org.springframework.security.core.context.SecurityContextHolder;
public class MySecurityUtil {
public static MyUser getCurrentUser(){ //其实MyUser存储在session中,如果你知道key值可以直接获取,手动笑脸
MyUser myUser = (MyUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return myUser;
}
}
八、登陆成功处理器
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.PropertyPreFilter;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import com.study.it.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component("success_handler")
public class MyAuthenctiationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
System.out.println("登录成功后:"+"自定义MyAuthenctiationSuccessHandler");
response.setCharacterEncoding("utf-8");//编码不要忘了,有了框架之后很容易忽略
//判断是重定向还是返回json
String accept = request.getHeader("Accept");
if(accept.indexOf("application/json")!=-1){ //ajax请求的时候返回json
MyUser myUser = (MyUser) authentication.getPrincipal();//获取MyUser类
//将MyUser类的User里的密码排除后的转成json格式
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(User.class);
filter.getExcludes().add("loginPwd");
String json = JSONObject.toJSONString(myUser.getUser(),new PropertyPreFilter[]{filter});
response.setContentType("application/json");
response.getWriter().print(json);
response.getWriter().close();
}else {//正常的时候跳转页面
//方案一:使用request或response跳转页面,可使用servlet基础的知识来做
// response.sendRedirect(request.getContextPath()+"/user/login");
//方案二:调用父类方法(onAuthenticationSuccess)跳转 ,注意default-target-url将不起作用
this.setDefaultTargetUrl("/check/checkin");//如是第一次登陆则跳转此路径
this.setAlwaysUseDefaultTargetUrl(false);//是否总是跳转到默认页面,fasle:跳转到登陆前一个页面
super.onAuthenticationSuccess(request,response,authentication);
}
}
}
九、登陆失败处理器
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component("failure_handler")
public class MyAuthenctiationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
System.out.println("进入MyAuthenctiationFailureHandler");
String accept = request.getHeader("Accept");//获取请求头
if(accept.indexOf("application/json")==-1){//不是json请求
String message = exception.getMessage().equals("Bad credentials")?"密码错误":exception.getMessage();
request.setAttribute("err",message);//存储错误信息到作用域中,异常信息包含账户不存在和密码错误信息
this.setDefaultFailureUrl("/WEB-INF/views/login.jsp");//跳转默认页面,不设报异常'No failure URL set, sending 401 Unauthorized error'
this.setUseForward(true);//页面跳转是否是内部转发,默认重定向行为
super.onAuthenticationFailure(request, response,exception);//父类方法完成跳转
//exception.printStackTrace();
}else {//ajax方式登录
response.setContentType("application/json");//设编码
response.setCharacterEncoding("utf-8");
//错误信息生成json格式输出
String json = String.format("{\"my,error\":\"%s\"}", exception.getMessage());
response.getWriter().print(json);
response.getWriter().close();
}
}
}
十、权限控制使用
1.注解
2.Jsp标签:如上图的注解差不多,对比即可了解
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<sec:authorize access="isAuthenticated()" var="islogin">
<sec:authentication property="principal.user" var="curuser"/>
欢迎您:<sec:authentication property="principal.username"/>
sec:authorize>
<c:if test="${islogin==false}">
<a href="${pageContext.request.contextPath}/login">请登录a>
c:if>
<sec:authorize access="hasAnyAuthority('ROLE_admin','ROLE_vip','edit_weibo')" var="ispass">
<a href="${pageContext.request.contextPath}/weibo/edit?id=${w.id}">修改a>
sec:authorize>
<c:if test="${ispass!=null&&ispass==false && curuser.id==w.user.id}">
<a href="${pageContext.request.contextPath}/weibo/edit?id=${w.id}">修改a>
c:if>
十一、免登陆
<sec:remember-me key="mykey" user-service-ref="myuserdetailservice" remember-me-parameter="remember_me"/>
<sec:remember-me data-source-ref="dataSource" token-validity-seconds="1209600" remember-me-parameter="remember_me" />
<input type="checkbox" name="remember_me" value="1"/>一周免登陆
十二、spring-security.xml中调用的类,备注
十三、登陆失败处理器中的异常
@PreAuthorize("isAuthenticated()"):必须是登陆的(自动登陆也算)
@PreAuthorize("isFullyAuthenticated()"):必须是手工登陆的(自动登陆不算)
@PreAuthorize("isRememberMe()"):必须是自动登陆的
@PreAuthorize("hasRole('USER')"):拥有ROLE_USER角色的(ROLE_会被自动加上)
@PreAuthorize("hasAuthority('ROLE_USER')")等同上面
@PreAuthorize("hasAnyRole('USER','VIP')"):拥有ROLE_USER或ROLE_VIP角色的
@PreAuthorize("hasAnyAuthority('ROLE_USER','ROLE_VIP','edit_weibo')"):等同上面
@PreAuthorize("hasRole('ADMIN') AND hasRole('DBA')"):拥有ROLE_ADMIN和ROLE_DBA角色的
@PreAuthorize("isFullyAuthenticated() and hasAnyAuthority('ROLE_vip','edit_weibo')")
@PreAuthorize("#contact.name == authentication.name") :参数contact的name属性和当前登录用户的用户名一致
@PreAuthorize("#c.name == authentication.name"):同上,只是起了别名c
public void doSomething(@P("c") Contact contact);
@PreAuthorize("#user.name.equals('abc')"):参数user的name属性是abc
public void add(User user)
@PreAuthorize("principal.username.equals(#username)"):当前登录用户名和参数username相吻合
@PreAuthorize("isFullyAuthenticated() and #uid==authentication.principal.user.id")
public User find(String username)
@PreAuthorize("isFullyAuthenticated()
and hasAnyAuthority('ROLE_vip','edit_weibo')
and #wb.user.id == authentication.principal.user.id"):综合的例子
@PostAuthorize ("returnObject.type == authentication.name"):运行后返回值的type属性和当前登录名一致
@PreFilter(filterTarget="ids", value="filterObject%2==0"):事先过滤ids集合参数,过滤条件是偶数值被使用
public void delete(List
@PreAuthorize("#id<10")//参数小于10才通过
public User find(int id)
@PostAuthorize("returnObject.id%2==0"):事后返回值的id属性是偶数才允许调用
public User find(int id) {
@PostFilter("filterObject.id%2==0"):返回值被过滤,只有偶数的数据才可返回
public List
终于到底部了,是不是很开心,如果你很贪心,觉得这个简单使用不够你胃口,我这里有一遍关于SpringSecurity安全框架更全面的资料,应该足够你看一天的了,待上传................