Spring Security 是为基于 Spring 的应用程序提供声明式安全保护的安全性框架,基于 Spring AOP 和 Servlet 过滤器实现,能够在 Web 请求级别和方法调用级别处理身份验证和授权。它使用 Servlet 过滤器保护 Web 请求和限制 URL 级别的访问,也可以使用 Spring AOP 保护方法调用——借助于对象代理和使用通知,能够确保只有具备适当权限的用户才能访问安全保护的方法。
web.xml
1
2
3
4
5
6
7
8
9
|
<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>
|
security.xml
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
|
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<global-method-security pre-post-annotations="enabled"/>
<http auto-config="false" use-expressions="true">
<intercept-url pattern="/u**" access="hasRole('ROLE_USER')"/>
<intercept-url pattern="/u/**" access="hasRole('ROLE_USER')"/>
<intercept-url pattern="/admin**" access="hasRole('ROLE_ADMIN')"/>
<form-login login-page="/index" default-target-url="/userhome" authentication-failure-url="/index?error=true"/>
<logout logout-url="/logout"/>
</http>
<authentication-manager>
<authentication-provider>
<password-encoder hash="bcrypt"/>
<jdbc-user-service data-source-ref="dataSource"
authorities-by-username-query="select user_tbl.user_name, role_tbl.name from user_tbl
join user_tbl_role_tbl on user_tbl.user_id=user_tbl_role_tbl.users_user_id
join role_tbl on user_tbl_role_tbl.roles_role_id = role_tbl.role_id
where user_tbl.user_name = ?"
users-by-username-query="select user_name, password, true from user_tbl where user_name = ?" />
<!--<user-service>-->
<!--<user name="admin" password="admin" authorities="ROLE_USER, ROLE_ADMIN" />-->
<!--</user-service>-->
</authentication-provider>
</authentication-manager>
</beans:beans>
|
User.java, Role.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
|
@Entity
@Table(name="user_tbl")
public class User implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@Column(name="user_id")
@GeneratedValue
private Long userId;
@Column(name="user_name")
@Size(min=3)
private String userName;
private String fullname;
@Size(min=1)
@Email
private String email;
@Size(min=3)
@Column(name="password")
private String password;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable
private List<Role> roles;
}
@Entity
@Table(name="role_tbl")
public class Role {
@Id
@Column(name="role_id")
@GeneratedValue
private Long userId;
private String name;
@ManyToMany(mappedBy = "roles")
private List<User> users;
}
|
HomeController.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
|
@Controller
public class HomeController {
static final Logger logger = (Logger) LoggerFactory.getLogger(HomeController.class);
@Autowired
private HibUserService hibUserService;
@Autowired
private HibSpittleService hibSpittleService;
@RequestMapping(value="index")
public String login(Model model) {
List<Spittle> spittleList = hibSpittleService.getRecentSpittles(5);
model.addAttribute("spittleList", spittleList);
return "index";
}
@RequestMapping(value="userhome")
public String loginSuccess(HttpServletRequest request, Principal principal) {
String username = principal.getName();
User user = hibUserService.findUserByName(username);
user.setLastIp(request.getRemoteAddr());
user.setLastVisit(new java.sql.Timestamp((new java.util.Date()).getTime()));
hibUserService.loginSuccess(user);
request.getSession().setAttribute("currentUser", user);
logger.info("User id: " + user.getUserId().toString());
return "redirect:u/" + username;
}
}
|
现在不登录访问 /u, /admin url 路径,都会跳到首页。
实现一个 Spittle 只有管理员和其作者才可以删除,在 Service 类上应用方法级安全措施
1
2
3
4
5
6
7
8
9
10
11
12
|
@Service
public class HibSpittleService {
@Autowired
private HibSpittleDao hibSpittleDao;
//...
@Transactional
@PreAuthorize("#spittle.user.userName == authentication.name or hasRole('ROLE_ADMIN')")
public void delete(@P("spittle") Spittle spittle) {
hibSpittleDao.delSpittle(spittle);
}
}
|
在与 AJAX 技术结合时,如果出现 Could not write JSON: failed to lazily initialize a collection of role,参考
https://github.com/FasterXML/jackson-datatype-hibernate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module;
public class HibernateAwareObjectMapper extends ObjectMapper {
public HibernateAwareObjectMapper() {
Hibernate4Module hm = new Hibernate4Module();
hm.configure(Hibernate4Module.Feature.FORCE_LAZY_LOADING, false);
registerModule(hm);
configure(SerializationFeature.INDENT_OUTPUT, true);
}
}
|